Setup

# library(ggplot2)
# library(reshape2)
library(ggpattern)
library(viridis)
library(colorRamps)
library(gridExtra)
library(ggplot2)
library('igraph')
library(ggnet)
library(network)
library(khroma)
library(dplyr)

source('similarity.R')

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

path.base = '../../../'
path.work = paste(path.base, '02_analysis/04_sv/01_data/', sep = '')
path.tair = paste(path.base, '01_data_common/01_tair10/', sep = '')
path.figures = paste(path.base, '02_analysis/04_sv/03_figures/', sep = '')
path.svs = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/svs/', sep = '')
# path.genes = paste(path.base, '01_data_common/02_annot_denovo/02_pannagram/genes/', sep = '')

# sim.cutoff = 0.9

sim.cutoff = 0.85

Coolors



fam.palette = c()
fam.palette['Unassigned'] = 'grey'
fam.palette['Mix'] = 'grey20'
fam.palette['Mix with Helitron'] = '#266D98'
fam.palette['Helitron'] = '#BCACDE'
fam.palette["LTR/Copia"] = '#BFDB38'
fam.palette["LTR/Gypsy"] = '#54B435'
fam.palette["DNA/HAT"] = '#F9B5D0'
fam.palette["DNA+"] = '#C8658C'
fam.palette["DNA/MuDR"] = '#971549'


fam.palette["LINE"] = '#FFC26F'
fam.palette["RathE1/2/3_cons"] = '#C38154'
fam.palette["SINE"] = '#884A39'
fam.palette["TEG"] = '#4E3636'

TEs


# Load similarity function

bl.file = paste(path.work,'new_te_on_te.fasta',sep = '')
bl.res = read.table(bl.file)
bl.res = bl.res[bl.res$V1 != bl.res$V8,]

bl.res.init = bl.res
bl.res = bl.res[bl.res$V6 >= sim.cutoff * 100,]

res.nest = findNestedness(bl.res, use.strand = F)
[1] 130447
[1] 17626
[1] 3789
[1] 1186
[1] 407
[1] 180
[1] 79
[1] 54
[1] 34
[1] 26
[1] 17
[1] 10
[1] 3
[1] 1
[1] 0
[1] 124919
[1] 17576
[1] 3842
[1] 1240
[1] 437
[1] 189
[1] 89
[1] 61
[1] 41
[1] 28
[1] 21
[1] 11
[1] 6
[1] 2
[1] 0
res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), function(s) as.numeric(strsplit(s, '\\|')[[1]][5]))
  
res.nest$len1 = res.nest.len[res.nest$V1]
res.nest$len8 = res.nest.len[res.nest$V8]
res.nest$p1 = res.nest$C1 / res.nest$len1
res.nest$p8 = res.nest$C8 / res.nest$len8

res.nest.sim = res.nest[(res.nest$p1 >= sim.cutoff) | 
                          (res.nest$p8 >= sim.cutoff),]

How many TEs are in the graph

Distribution among families and subfamilies Distribution among lengths

te.in.graph = unique(c(res.nest$V1, res.nest$V8))

# What is the actual number of TEs
file.content <- readLines(bl.file)

selected.lines <- file.content[grepl("^# Query:|hits found", file.content)]
df.query = data.frame(b.query=selected.lines[seq(1, length(selected.lines), by = 2)],
                      b.hits=selected.lines[seq(2, length(selected.lines), by = 2)])

df.query$query  <- gsub("^# Query: (.*)", "\\1", df.query$b.query)
df.query$len <- as.numeric(sapply(strsplit(df.query$query, "\\|"), function(x) x[5]))
df.query$hits <- as.numeric(stringr::str_extract(df.query$b.hits, "\\d+"))
df.query$val.hits = df.query$hits
df.query$val.hits[df.query$val.hits >= 2] = 2
df.query$val.hits[df.query$query %in% bl.res$V8] = 2
df.query$val.hits[df.query$query %in% te.in.graph] = 3
hit.values = c('0 hits', '1 self-hit', 'partial overlap', 'in graph', "in graph but not in SVs")
df.query$s.hits = hit.values[df.query$val.hits+1]
df.query$s.hits = factor(df.query$s.hits, levels = rev(hit.values))
df.query$family <- sapply(strsplit(df.query$query, "\\|"), function(x) x[9])
df.query$subfam <- sapply(strsplit(df.query$query, "\\|"), function(x) x[8])


my_colors <- colors <- c("in graph" = "#676FA3",
            "partial overlap" = "#FF9F29",
            "1 self-hit" = "#6EBF8B",
            "0 hits" = "#D82148",
            "in graph but not in SVs" = "#151D3B")


# TEs, which are not in SVs
te.in.svs = read.table(paste(path.work, 'blast_tes_on_sv.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(df.query$query, te.in.svs$V1)
te.in.svs = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''), stringsAsFactors = F)
te.rest = setdiff(te.rest, te.in.svs$V8)
df.query$s.hits[df.query$query %in% te.rest] = "in graph but not in SVs"


p = ggplot(df.query, aes(x = len, fill = s.hits, color = s.hits)) +
  # geom_histogram(aes(y = ..density..), alpha=0.5, color = "black", bins = 30) +
  # geom_jitter(height = 0.02, width = 0, alpha = 0.7) +
  geom_density(alpha = 0.5) +
  scale_fill_manual(values = my_colors) +
  scale_color_manual(values = my_colors) +
   scale_x_log10() +
  labs(fill = NULL, color = NULL) +
  xlab('length of TEs') + ylab('Normalised density') +
  theme_minimal() +
  theme(legend.position = c(1, 1), legend.justification = c(1, 1),
          legend.background = element_rect(color = "grey90"))

p

pdf(paste(path.figures, 'tes_self_blast_len_density.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

table(df.query$val.hits)

    0     1     2     3 
 1584 13615   766 19125 

TEs not in SVs

# TEs in te-graph: te.in.graph
# TEs which are have no connection to SVs

df = as.data.frame(table(df.query$s.hits))

  
colors <- c("in graph" = "#676FA3",
            "partial overlap" = "#FF9F29",
            "1 self-hit" = "#6EBF8B",
            "0 hits" = "#D82148",
            "in graph but not in SVs" = "#151D3B")


p = ggplot(df, aes(x = "", y = Freq, fill = Var1)) +
  geom_bar(stat="identity", width=1, alpha = 0.7) +
  coord_polar("y", start=0) +
  labs(title=NULL, fill="Categories") +
  theme_void()+
    scale_fill_manual(values = colors) +
  geom_text(aes(label = Freq,x = 1.3), position = position_stack(vjust = 0.5)) + theme(legend.position="none")
p


pdf(paste(path.figures, 'tes_self_blast_pie_chart.pdf', sep = ''), width = 3, height = 3)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Examples

Examples no hits


pdf(paste(path.figures, 'tes_self_scatter_no_hits_long.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

head(df.query.tmp[df.query.tmp$family == 'DNA/MuDR',]$query)
[1] "te|641277|641420|1|144|+|AT1TE02080|ARNOLDY1|DNA/MuDR"     
[2] "te|1157763|1157863|1|101|+|AT1TE03780|LIMPET1|DNA/MuDR"    
[3] "te|4546279|4546388|1|110|+|AT1TE14750|ARNOLD2|DNA/MuDR"    
[4] "te|6753991|6754119|1|129|-|AT1TE21830|ATDNAI27T9A|DNA/MuDR"
[5] "te|10655834|10655963|1|130|+|AT1TE34455|ARNOLD1|DNA/MuDR"  
[6] "te|11660991|11661122|1|132|+|AT1TE37760|ATDNA2T9C|DNA/MuDR"

Examples one self-hit

families



df.query.tmp = df.query[(df.query$val.hits == 1),]

cnt.init = c(table(df.query$family))
cnt.tmp = c(table(df.query.tmp$family))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_fam.pdf', sep = ''), width = 5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

subfamilies



df.query.tmp = df.query[(df.query$val.hits == 1) & (df.query$len >= 600),]

cnt.init = c(table(df.query$subfam))
cnt.tmp = c(table(df.query.tmp$subfam))

common_names <- intersect(names(cnt.init), names(cnt.tmp))
# Создание dataframe только для совпадающих имен
df_match <- data.frame(names = common_names, values.init = cnt.init[common_names], 
                       values.tmp = cnt.tmp[common_names])


# gradient_colors <- c(discrete_rainbow(nrow(df_match)))
names(gradient_colors) = NULL


p = ggplot(df_match, aes(x = values.init, y = values.tmp, label = names, color = names)) +
  geom_point() +
  # geom_text(hjust = 0, vjust = 0) +
  ggrepel::geom_text_repel(max.overlaps = 20) +
  xlab("Initial counts") +
  ylab("Counts in \"1 self-hits\" category") +
  scale_x_log10() +
  scale_y_log10() +
  # scale_color_manual(values = gradient_colors) +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


pdf(paste(path.figures, 'tes_self_scatter_1_selfhits_subfam.pdf', sep = ''), width = 7, height = 5)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

individuals from subfamilies

s.subfam = 'ATREP8'

df.query.tmp = df.query[(df.query$subfam == s.subfam) & (df.query$len >= 600),]
df.query.tmp

Creating the graph

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )

te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

edges = edges[te.enges.fam[edges[,1]] != 'TEG',]
edges = edges[te.enges.fam[edges[,2]] != 'TEG',]
te.enges.names = unique(c(edges[,1], edges[,2]))


# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.nodes = te.nodes[te.enges.fam[te.nodes[,1]] != 'TEG',]
te.nodes = te.nodes[te.enges.fam[te.nodes[,2]] != 'TEG',]

te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), 
                   te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te


nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  fam.te = unique(te.enges.fam[s.te])
  if(length(fam.te) == 1){
    return(fam.te)
  } else {
    fam.te = setdiff(fam.te, 'TEG')
    if(length(fam.te) == 1) return(fam.te)
    return('Mix')
  }
})
table(nodes.cnt$fam)

            DNA        DNA/MuDR        Helitron            LINE       LTR/Copia       LTR/Gypsy             Mix 
           1109            1228            2302             356             503            1837              67 
RathE1/2/3_cons            SINE 
             53              27 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 7245
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
[1] 9000
[1] 10000
[1] 11000
[1] 12000
[1] 13000
[1] 14000
[1] 15000
[1] 16000
[1] 17000
[1] 18000
[1] 19000
[1] 20000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node

g.cols = discrete_rainbow(length(unique(g.nodes.fam)))
names(g.cols) = unique(g.nodes.fam)

b.graph.init = b.graph


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

Old colors

p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.fam[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) 
Loading required package: sna
Loading required package: statnet.common

Attaching package: ‘statnet.common’

The following objects are masked from ‘package:base’:

    attr, order

sna: Tools for Social Network Analysis
Version 2.7-1 created on 2023-01-24.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
 For citation information, type citation("sna").
 Type help(package="sna") to get started.


Attaching package: ‘sna’

The following objects are masked from ‘package:igraph’:

    betweenness, bonpow, closeness, components, degree, dyad.census, evcent, hierarchy, is.connected,
    neighborhood, triad.census

Loading required package: scales

Attaching package: ‘scales’

The following object is masked from ‘package:viridis’:

    viridis_pal

Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 6909 > 1' in coercion to 'logical(1)'
p + guides(size = F)


# 
# b.graph.fam = cbind(g.nodes.fam[b.graph[,1]], g.nodes.fam[b.graph[,2]])
# b.graph.fam
# 
# which((b.graph.fam[,1] == 'DNA/MuDR') & (b.graph.fam[,1] == 'LINE'))

New Family colors

g.fam.names = sort(unique(g.nodes.fam))
fam.palette = c()
idx.pallete = c()

idx.fam <- grep("^Helitron", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#BFACE2', '#266D98', '#422B72'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam <- grep("^LTR", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#BFDB38', '#54B435'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam <- grep("^DNA", g.fam.names, value = FALSE)
tmp.palette <- colorRampPalette(c('#F9B5D0', '#971549'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

idx.fam = setdiff(1:length(g.fam.names), idx.pallete)
tmp.palette <- colorRampPalette(c('#FFC26F', '#C38154', '#884A39', '#4E3636'))(length(idx.fam))
idx.pallete = c(idx.pallete, idx.fam)
fam.palette = c(fam.palette, tmp.palette)

names(fam.palette) = g.fam.names[idx.pallete]
fam.palette['Unassigned'] = 'grey'
fam.palette['Mix'] = 'black'
fam.palette['TEG'] = 'darkgreen'

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   2   51  209  176  104   29 
3493   40   38   37   32   31 
k = 1
tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.big = network.vertex.names(g.part.sub.big)


set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3493 > 1' in coercion to 'logical(1)'
p.big.type = p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.big], 
#             color = g.nodes.fam[b.graph.names.sub.big],
#             mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.big.color = p + theme(legend.position = "none")


tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership != tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.small <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.small = network.vertex.names(g.part.sub.small)


set.seed(20)
p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3416 > 1' in coercion to 'logical(1)'
p.small.type =p + theme(legend.position = "none")

# set.seed(20)
# p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
#             node.size = g.nodes.cnt[b.graph.names.sub.small], 
#             color = g.nodes.fam[b.graph.names.sub.small],
#             # mode = 'kamadakawai',
#             palette = fam.palette) + guides(size = F)
# p.small.color = p + theme(legend.position = "none")

Plots

p.big.type

p.small.type


pdf(paste(path.figures, 'graph_tes_family_small.pdf', sep = ''), width = 9, height = 9)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 
pdf(paste(path.figures, 'graph_tes_family_big.pdf', sep = ''), width = 5, height = 5)
print(p.big.type)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Stop for the paper

stop()

Specific TE families

Graph of one family

sort(-table(df.query$subfam[(df.query$val.hits == 3) & (df.query$family == 'LTR/Copia')]))

     META1  ATCOPIA95  ATCOPIA57  ATCOPIA28  ATCOPIA41  ATCOPIA49  ROMANIAT5  ATCOPIA94  ATCOPIA13  ATCOPIA37 
       -58        -51        -34        -33        -28        -28        -28        -27        -23        -22 
 ATCOPIA65  ATCOPIA43  ATCOPIA35  ATCOPIA42  ATCOPIA69  ATCOPIA78  ATCOPIA27  ATCOPIA58      ATRE1  ATCOPIA29 
       -22        -21        -16        -15        -15        -15        -14        -14        -14        -13 
 ATCOPIA54  ATCOPIA45  ATCOPIA51  ATCOPIA66  ATCOPIA75  ATCOPIA12  ATCOPIA36  ATCOPIA50  ATCOPIA67   ENDOVIR1 
       -12        -11        -11        -11        -11        -10        -10        -10        -10        -10 
 ATCOPIA16  ATCOPIA21  ATCOPIA22  ATCOPIA34   ATCOPIA4  ATCOPIA44  ATCOPIA48  ATCOPIA62  ATCOPIA63  ATCOPIA64 
        -9         -9         -9         -9         -9         -9         -9         -9         -9         -9 
 ATCOPIA87  ATCOPIA11  ATCOPIA55  ATCOPIA61  ATCOPIA70  ATCOPIA15  ATCOPIA25  ATCOPIA30  ATCOPIA52  ATCOPIA93 
        -9         -8         -8         -8         -8         -7         -7         -7         -7         -7 
 ATCOPIA96  ATCOPIA26  ATCOPIA31  ATCOPIA56  ATCOPIA8A   ATCOPIA9   ATCOPIA1  ATCOPIA10  ATCOPIA23   ATCOPIA3 
        -7         -6         -6         -6         -6         -6         -5         -5         -5         -5 
ATCOPIA31A  ATCOPIA38  ATCOPIA40   ATCOPIA5  ATCOPIA83  ATCOPIA89  ATCOPIA91  ATCOPIA97  ATCOPIA14  ATCOPIA17 
        -5         -5         -5         -5         -5         -5         -5         -5         -4         -4 
  ATCOPIA2  ATCOPIA24  ATCOPIA32  ATCOPIA33 ATCOPIA38B  ATCOPIA39  ATCOPIA46  ATCOPIA68  ATCOPIA74  ATCOPIA88 
        -4         -4         -4         -4         -4         -4         -4         -4         -4         -4 
 ATCOPIA8B  ATCOPIA90  ATCOPIA19  ATCOPIA60  ATCOPIA72  ATCOPIA76  ATCOPIA77  ATCOPIA79  ATCOPIA82  ATCOPIA85 
        -4         -4         -3         -3         -3         -3         -3         -3         -3         -3 
 ATCOPIA86      TA1-2  ATCOPIA18 ATCOPIA18A  ATCOPIA20 ATCOPIA32B  ATCOPIA47  ATCOPIA53  ATCOPIA59 ATCOPIA65A 
        -3         -3         -2         -2         -2         -2         -2         -2         -2         -2 
 ATCOPIA71  ATCOPIA73  ATCOPIA81  ATCOPIA92 ATCOPIA38A   ATCOPIA6   ATCOPIA7  ATCOPIA80  ATCOPIA84 
        -2         -2         -2         -2         -1         -1         -1         -1         -1 

# one.te.fam = 'BRODYAGA1'
# one.te.fam = 'BRODYAGA2'
# one.te.fam = 'HELITRONY1D'
# one.te.fam = 'HELITRONY3'
one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]


one.te.fam = 'ATCOPIA41'
query.fam = df.query$query[df.query$subfam == one.te.fam]

res.nest.famp = res.nest[(res.nest$V1 %in% query.fam) | (res.nest$V8 %in% query.fam),]


idx = res.nest.famp$p1 >= sim.cutoff
edges = cbind(res.nest.famp$V1[idx], res.nest.famp$V8[idx])
idx = res.nest.famp$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest.famp$V8[idx], res.nest.famp$V1[idx]))


te.enges.names = unique(c(edges[,1], edges[,2]))
te.enges.fam = sapply(te.enges.names, function(s) strsplit(s, '\\|')[[1]][9] )
te.enges.fam[te.enges.fam %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
te.enges.fam[te.enges.fam %in% c('RathE1_cons', 'RathE2_cons', 'RathE3_cons')] = 'RathE1/2/3_cons'
te.enges.fam[te.enges.fam %in% c('LINE/L1', 'LINE?')] = 'LINE'
te.enges.fam[te.enges.fam %in% c('Unassigned')] = 'Mix'
te.enges.fam[te.enges.fam %in% c('RC/Helitron')] = 'Helitron'

g.part <- network(edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)
b.graph.len = as.numeric(sapply(strsplit(b.graph.names, "\\|"), function(x) x[5]))


label.family = sapply(strsplit(b.graph.names, "\\|"), function(x) x[8])
lab.cols = c('#3F2E3E', "white")
label.color = lab.cols[(label.family == one.te.fam) + 1]

set.seed(20)
p <- ggnet2(g.part, label = b.graph.len, edge.color = "black", 
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names], 
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '.pdf', sep = ''), width = 20, height = 18)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

set.seed(20)
p <- ggnet2(g.part, label = b.graph.names, edge.color = "black",
             node.size = 15,
            alpha=0.8,
            arrow.gap = 0.015,
            arrow.size = 5,
            # label.color = label.color,
            # node.size = g.nodes.cnt[b.graph.names],
            color = te.enges.fam[b.graph.names],
            palette = fam.palette,
            # mode = "kamadakawai"
            ) + guides(size = F)

pdf(paste(path.figures, 'real_tes_subfam_', one.te.fam, '_names.pdf', sep = ''), width = 50, height = 49)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

Dotplots

Functions



seq2mx <- function(seq, wsize){
  
  num_rows <- length(seq) - wsize + 1
  matrix_seq <- matrix(nrow = num_rows, ncol = wsize)
  for (i in 1:num_rows) {
    matrix_seq[i, ] <- seq[i:(i + wsize - 1)]
  }

  return(matrix_seq)
}

mxComp <- function(mx1, mx2, wsize, nmatch){
  mx.res = 0
  for(s in c('A', 'C', 'G', 'T')){
    mx.res = mx.res + (mx1 == s) %*% t(mx2 == s)
  }
  # mx.res = (mx.res >= nmatch) * 1
  mx.res[mx.res < nmatch] = 0
  
  indices <- which(mx.res != 0, arr.ind = TRUE)
  values <- mx.res[indices]
  result <- cbind(indices, values)
  result = as.data.frame(result)
  return(result)
}

dotplot <- function(seq1, seq2, wsize, nmatch) {
  seq2.rc = rev(seqinr::comp(seq2))

  mx1 = toupper(seq2mx(seq1, wsize))
  mx2 = toupper(seq2mx(seq2, wsize))
  
  result = mxComp(mx1, mx2, wsize, nmatch)
  
  mx2.rc = toupper(seq2mx(seq2.rc, wsize))
  result.rc = mxComp(mx1, mx2.rc, wsize, nmatch)
  result.rc$values = -result.rc$values
  result.rc$col = length(seq2) - result.rc$col - wsize + 2
  result = rbind(result.rc, result)
  
  
  p = ggplot(result, aes(x = row, y = col, fill = values)) +
    geom_tile(width = 1, height = 1) +
    # xlab(name1) + ylab(name2) +
    # xlab(paste0(strsplit(name1, '\\|')[[1]][7:9], collapse = '|')) + 
    # ylab(paste0(strsplit(name2, '\\|')[[1]][7:9], collapse = '|')) +
    # xlab('') + ylab('') +
    guides(fill = FALSE) +
    theme_minimal() + coord_fixed() +
    scale_x_continuous(expand = c(0, 0)) + 
    scale_y_continuous(expand = c(0, 0)) +
    scale_fill_gradient2(low = "#CE1F6A", mid = "white", high = "#27374D", midpoint = 0) +
    theme(panel.border = element_rect(colour = "grey", fill=NA, size=1))
  p 
  return(p)
}
file.te.fasta = '/Volumes/Samsung_T5/vienn/tair/new_filtration/new_te.fasta'
te.fasta = seqinr::read.fasta(file.te.fasta)
te.names = names(te.fasta)
te.fasta = seqinr::getSequence(te.fasta)
names(te.fasta) = te.names

One pairwise example

wsize = 10
nmatch = 8

seq1 = te.fasta[[b.graph.names[1]]]
seq2 = te.fasta[[b.graph.names[1]]]


name1 = 'te|12384763|12385262|4|500|+|AT4TE57580|BRODYAGA1A|DNA/MuDR'
name2 = 'te|13674917|13675271|1|355|+|AT1TE44760|BRODYAGA1|DNA/MuDR'

# name1 = 'te|10592111|10592664|1|554|-|AT1TE34265|BRODYAGA2|DNA/MuDR'
# name2 = 'te|8743238|8744263|4|1026|-|AT4TE39045|HELITRONY1D|RC/Helitron'

name1 = 'te|6283198|6284421|4|1224|-|AT4TE26710|ATREP15|RC/Helitron'
name2 = 'te|6283198|6284421|4|1224|-|AT4TE26710|ATREP15|RC/Helitron'

seq1 = te.fasta[[name1]]
seq2 = te.fasta[[name2]]

p = dotplot(seq1, seq2, wsize, nmatch)

p = p + annotate("text", x = -Inf, y = Inf, label = paste('wsize=',wsize,'\nnmatch=',nmatch, sep = ''), 
             hjust = -0.1, vjust = 1.1)

p

pdf(paste(path.figures, 'pairwise_','.pdf', sep = ''), width = 5, height = 5)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

one VS all


wsize = 10
nmatch = 8

name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = 'te|16691748|16695154|1|3407|−|AT1TE55070|ATCOPIA41|LTR/Copia'
name0 = gsub('−', "-", name0)


one.te.fam = strsplit(name0, '\\|')[[1]][8]
# one.te.fam = 'BRODYAGA2'
query.fam = df.query$query[df.query$subfam == one.te.fam]
query.fam = query.fam[(query.fam %in% res.nest.sim$V1) | (query.fam %in% res.nest.sim$V2)]

names.all = setdiff(query.fam, name0)

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = strsplit(name0, '\\|')[[1]][7]
  s2 = strsplit(name2, '\\|')[[1]][7]
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}
# 
# pp = grid.arrange(grobs = p.all, ncol = 13) ## display plot
# 
# 
# pdf(paste(path.figures, 'pairwise_all','.pdf', sep = ''), width = 50, height = 50)
# print(pp)     # Plot 1 --> in the first page of PDF
# dev.off()

s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_all_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file

one connected component


wsize = 10
nmatch = 8


name0 = 'te|6205621|6206184|2|564|−|AT2TE25255|HELITRONY1D|RC/Helitron'
name0 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name0 = 'te|12513239|12513824|1|586|+|AT1TE40725|ATHILA4A|LTR/Gypsy'
name0 = 'te|11647426|11648912|1|1487|+|AT1TE37705|ATREP7|RC/Helitron'
name0 = 'te|11683565|11689821|3|6257|+|AT3TE48540|ATCOPIA95|LTR/Copia'
name0 = gsub('−', "-", name0)


names.all = unique(c(res.nest.sim$V1[res.nest.sim$V8 == name0],
                     res.nest.sim$V8[res.nest.sim$V1 == name0]))
# names.all = unique(c(res.nest$V1[res.nest$V8 == name0], 
#                      res.nest$V8[res.nest$V1 == name0]))

p.all = list()
for(name2 in names.all){
  # message(name2)
  seq1 = te.fasta[[name0]]
  seq2 = te.fasta[[name2]]
  
  s1 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '|')
  s2 = paste0(strsplit(name2, '\\|')[[1]][7:9], collapse = '|')
  p = dotplot(seq1, seq2, wsize, nmatch) + xlab(s1) + ylab(s2)
  p.all[[name2]] = p
}


s0 = paste0(strsplit(name0, '\\|')[[1]][7:9], collapse = '_')
s0 = gsub("/", "-", s0)
pdf(paste(path.figures, 'pairwise_connect_',s0,'.pdf', sep = ''), width = 50, height = 50)
grid.arrange(grobs = p.all, ncol = ceiling(sqrt(length(p.all)))) # Write the grid.arrange in the file
dev.off() # Close the file


name1 = 'te|14189256|14190266|5|1011|-|AT5TE50700|HELITRONY3|RC/Helitron'
name2 = 'te|2162295|2162937|2|643|-|AT2TE09950|HELITRONY3|RC/Helitron'
name0 = name1

names.all = unique(c(res.nest$V1[res.nest$V8 == name0], res.nest$V8[res.nest$V1 == name0]))


names = c(name1, name2)
b.tmp = bl.res[(bl.res$V1 %in% names) & (bl.res$V8 %in% names),]

res.nest[(res.nest$V1 %in% names) & (res.nest$V8 %in% names), ]

SVs

Readings seSVs


sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))

# Rename length groups
lev.replace = c('[1,10]', '(10,15]')
lev.new = '[1,15]'

s.levels = as.character(levels(sv.se$len.gr))
s.levels = s.levels[!(s.levels %in% lev.replace)]
s.levels = c(lev.new, s.levels)
s.levels = gsub("e\\+03", "k", s.levels)

sv.se$len.gr = as.character(sv.se$len.gr)
sv.se$len.gr[sv.se$len.gr %in% lev.replace] = lev.new
sv.se$len.gr = gsub("e\\+03", "k", sv.se$len.gr)
sv.se$len.gr = factor(sv.se$len.gr, levels = s.levels)


# Replace families
sv.se$fam = as.character(sv.se$fam)
sv.se$fam <- gsub("Helitron/.*", "Mix with Helitron", sv.se$fam)


sv.se$te = factor(sv.se$te, levels = c('isTE', 'isTEpart', 'hasTE', 'hasTEpart', 'noTE'))

Reading nestedness


# Load similarity function

file.nestedness = paste(path.work, 'sv_big_on_big_nest.rds', sep = '')


if(!file.exists(file.nestedness)){
  bl.file = paste(path.work, 'sv_big_on_big.txt', sep = '')
  bl.res = read.table(bl.file)
  bl.res = bl.res[bl.res$V1 != bl.res$V8,]

  res.nest = findNestedness(bl.res, use.strand = F)
    
  res.nest$len1 = res.nest.len[res.nest$V1]
  res.nest$len8 = res.nest.len[res.nest$V8]
  res.nest$p1 = res.nest$C1 / res.nest$len1
  res.nest$p8 = res.nest$C8 / res.nest$len8  
  saveRDS(res.nest, file.nestedness, compress = F)
} else {
  res.nest = readRDS(file.nestedness)
}

res.nest.len = sapply(unique(c(res.nest$V1, res.nest$V8)), 
                      function(s) as.numeric(strsplit(s, '\\|')[[1]][2]))
res.nest0 = res.nest

TE stat

In graph - not in graph

res.nest = res.nest0

sv.se.len = sv.se[sv.se$len >= 100,]
sv.se.len$in.connect = sv.se.len$name %in% names(res.nest.len)

cnt.sv.se = table(sv.se.len$in.connect , sv.se.len$te)
cnt.sv.se
       
        isTE isTEpart hasTE hasTEpart noTE
  FALSE   41      682   220       454 5615
  TRUE  4299     2627  2864      1468 2049
df = reshape2::melt(cnt.sv.se)

te.content.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
cols = c('#D8D9CF', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
names(cols) = te.content.names

df$Var2 = factor(df$Var2, levels = rev(c('isTE', 'isTEpart', 'hasTE', 'hasTEpart', 'noTE')))


# install.packages("ggpattern")


p = ggplot(df, aes(x = Var2, y = value, fill = Var2, alpha = Var1, color = Var1)) +
  geom_col_pattern( aes(pattern = Var1),
    # pattern = rep(c('none', "stripe"), 5),
    pattern_density = 0.1,
    pattern_spacing = 0.025,
    pattern_fill = "grey70", 
    position = "dodge", 
    width = 0.8
  ) + 
  # geom_col(position = "dodge", width = 0.8) +
  scale_alpha_manual(values = c(0.8, 1), labels = c("No", "Yes")) +
  scale_color_manual(values = c('black', 'black'), labels = c("not in graph", "in graph")) +
  scale_pattern_manual(values = c("stripe", 'none'), labels = c("in graph", "not in graph"),
                       breaks = c(TRUE, FALSE)) +
  labs(fill = "", pattern='Connected to others') +
  scale_fill_manual(values = cols) +
  xlab(NULL) +
  ylab("Number of SVs") +
  theme(axis.text.y = element_blank()) + 
  guides(alpha = "none", fill = 'none', color = 'none') +
  theme_minimal() + coord_flip() +
  theme(
    legend.position = c(0.7, 0.3),     # Adjust these coordinates as needed
    legend.background = element_rect(fill="transparent", color='grey70')  
  ) +
  theme(axis.text.y = element_blank()) +
  guides(pattern = guide_legend(override.aes = list(fill = c("white"), color= 'black')))  
p

pdf(paste(path.figures, 'graph_mob_in_graph.pdf', sep = ''), width = 3, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

TE families in SV types

pp = ggpubr::ggarrange(p + xlab('TE content') + scale_x_discrete(labels = c('is compl.', 'is fragm.', 
                               'cont. compl.', 'cont. fragm.')) , g, ncol = 2, widths = c(0.75, 0.25))
pp

pdf(paste(path.figures, 'graph_mob_te_fam_sv_type.pdf', sep = ''), width = 6, height = 4)
print(pp)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

TE fam: TAIR10


p <- ggplot(df, aes(x = ref, y = value, color = names)) +
  geom_smooth(aes(group = 1), method = "lm", formula = y ~ x, se = FALSE, color = 'grey70') + 
  geom_point() +
  ggrepel::geom_text_repel(aes(label = names), max.overlaps = 20) +
  # xlab("log # in TAIR10 annotation") +
  # ylab("log # in SVs") +
  # scale_x_log10() +
  # scale_y_log10() +
  xlab("# in TAIR10 annotation") +
  ylab("# in SVs") +
  theme(legend.position = "none") +
  guides(color = FALSE) +
  theme_minimal()
p


lm_model <- lm(value ~ ref, data = df)
slope <- coef(lm_model)[2]


p = p + annotate("text", x = min(df$ref), y = max(df$value), 
           label = paste('Slope:', round(slope, 3)), hjust = 0, vjust = 1)



pdf(paste(path.figures, 'graph_mob_te_fam_tair10.pdf', sep = ''), width = 4, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Filtration


res.nest = res.nest0

sv.names.mix = sv.se$name[grep("^Mix", sv.se$fam)]
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]


sv.names.mix = sv.se$name[sv.se$te == 'noTE']
res.nest = res.nest[!(res.nest$V1 %in% sv.names.mix),]
res.nest = res.nest[!(res.nest$V8 %in% sv.names.mix),]

singleton.mode = F
if(singleton.mode){
  sv.names.freq = sv.se$name[sv.se$freq.max <= 3]
  # sv.names.freq = sv.se$name[sv.se$freq.max >= 25]
  res.nest = res.nest[res.nest$V1 %in% sv.names.freq,]
  res.nest = res.nest[res.nest$V8 %in% sv.names.freq,]
}

prefix.mode = c('', '_single')

Graph

# all edges
idx = res.nest$p1 >= sim.cutoff
edges = cbind(res.nest$V1[idx], res.nest$V8[idx])
idx = res.nest$p8 >= sim.cutoff
edges = rbind(edges, cbind(res.nest$V8[idx], res.nest$V1[idx]))
te.enges.names = unique(c(edges[,1], edges[,2]))

tmp = sv.se$te
names(tmp) = sv.se$name
te.enges.type = as.character(tmp[te.enges.names])
names(te.enges.type) <- names(tmp[te.enges.names])


tmp = sv.se$fam
names(tmp) = sv.se$name
te.enges.fam = tmp[te.enges.names]

# nodes
idx = (res.nest$p1 >= sim.cutoff) & (res.nest$p8 >= sim.cutoff)
te.nodes = cbind(res.nest$V1[idx], res.nest$V8[idx])
te.rest = setdiff(te.enges.names, c(te.nodes[,1], te.nodes[,2]))


te.nodes.graph <- igraph::make_graph(t(te.nodes), directed = T)
te.nodes.graph <- igraph::simplify(te.nodes.graph)
te.nodes.comp <- igraph::components(te.nodes.graph)

nodes = data.frame(node = paste('N', te.nodes.comp$membership, sep = ''), te = names(te.nodes.comp$membership))

nodes.rest = data.frame(node = paste('R', (1:length(te.rest)), sep = ''), te = te.rest)
nodes = rbind(nodes, nodes.rest)

rownames(nodes) = nodes$te

# Define TE type
nodes.cnt = data.frame(cnt = c(table(nodes$node)))
nodes.cnt$node = rownames(nodes.cnt)
nodes.cnt$type = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.type[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$type)

    hasTE hasTEpart      isTE  isTEpart 
     1043       466       433      1884 
# Define TE family
nodes.cnt$fam = sapply(nodes.cnt$node, function(s){
  s.te = nodes$te[nodes$node == s]
  type.te = unique(te.enges.fam[s.te])
  if(length(type.te) == 1){
    return(type.te)
  } else {
    type.te = table(type.te)
    type.te = names(type.te)[type.te == max(type.te)]
    return(type.te[1])
  }
})
table(nodes.cnt$fam)

        DNA/HAT        DNA/MuDR            DNA+        Helitron            LINE       LTR/Copia       LTR/Gypsy 
            127             697             356             818             441             442             702 
RathE1/2/3_cons            SINE             TEG      Unassigned 
             30              12             148              53 
# Redefine edges but with node names
idx.endes = (edges[,1] %in% nodes$te) & (edges[,2] %in% nodes$te)
b.graph = cbind(nodes[edges[idx.endes,1], 'node'],nodes[edges[idx.endes,2], 'node'])
b.graph = unique(b.graph)
# b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph.uni = b.graph[b.graph[,1] == b.graph[,2],]
b.graph = b.graph[b.graph[,1] != b.graph[,2],]

length(unique(c(b.graph[,1], b.graph[,2])))
[1] 3626
# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
[1] 1000
[1] 2000
[1] 3000
[1] 4000
[1] 5000
[1] 6000
[1] 7000
[1] 8000
[1] 9000
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]
# b.graph = rbind(b.graph, b.graph.uni)

# Print graph

g.nodes.type = nodes.cnt$type
names(g.nodes.type) = nodes.cnt$node
g.nodes.cnt = nodes.cnt$cnt
names(g.nodes.cnt) = nodes.cnt$node
g.nodes.fam = nodes.cnt$fam
names(g.nodes.fam) = nodes.cnt$node


g.cols.names = c("noTE", "isTE", "hasTE", "hasTEpart", "isTEpart")
g.cols = c('#FFD966', '#EB455F', '#7B6079', '#3C8DAD', '#79B773')
names(g.cols) = g.cols.names


g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.type[b.graph.names],
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = g.cols) + guides(size = F)
Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3626 > 1' in coercion to 'logical(1)'
p

# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

# set.seed(20)
# p <- ggnet2(g.part, label = F, edge.color = "grey30", 
#             node.size = g.nodes.cnt[b.graph.names], 
#             color = c('TE', 'noTE')[(g.nodes.type[b.graph.names] == 'noTE')*1+1],
#             # mode = 'kamadakawai',
#             # arrow.gap = 0, 
#             # arrow.size = 3,
#             palette = c('noTE' = 'black', 'TE' = '#AEC3AE')) + guides(size = F)
# p
# 
# # path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_mob_all_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
#     width = 5, height = 5)
# print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
# dev.off()

Colored by TE family


if(length(setdiff(g.nodes.fam, names(fam.palette)))!=0) stop('not all families are defined')

set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "grey20", 
            node.size = g.nodes.cnt[b.graph.names], 
            color = g.nodes.fam[b.graph.names],
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 3993 > 1' in coercion to 'logical(1)'
p = p + theme(legend.text = element_text(size = 8), 
          legend.title = element_blank(),
          legend.key.size = unit(0.5, "cm")) + guides(color = guide_legend(ncol = 2))
p


pdf(paste(path.figures, 'graph_mob_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 
pdf(paste(path.figures, 'graph_mob_cluster', prefix.mode[singleton.mode+1] ,'_family_legend.pdf', sep = ''), width = 7, height = 5)
print(p+ coord_fixed(ratio = 1))     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Node size distribution

df = data.frame(node = unique(nodes$node))
df$size = g.nodes.cnt[df$node]
df$fam = g.nodes.fam[df$node]
df$type = g.nodes.type[df$node]

fam.palette
       Unassigned               Mix Mix with Helitron          Helitron         LTR/Copia         LTR/Gypsy 
           "grey"          "grey20"         "#266D98"         "#9581BC"         "#BFDB38"         "#54B435" 
          DNA/HAT              DNA+          DNA/MuDR              LINE   RathE1/2/3_cons              SINE 
        "#F9B5D0"         "#C8658C"         "#971549"         "#FFC26F"         "#C38154"         "#884A39" 
              TEG 
        "#4E3636" 
p = ggplot(df, aes(x = type, y = size, color=fam)) +
  geom_jitter(width = 0.2) +
  labs(x = "Type", y = "Size") + 
  scale_y_continuous(trans = "log2") +
  scale_color_manual(values = fam.palette)+
  theme_minimal() +
  guides(color = guide_legend(ncol = 2)) +
  labs(color = "TE family") + xlab('') + ylab('Node size (Number of similar SVs)')
p


pdf(paste(path.figures, 'graph_mob_size_distribution.pdf', sep = ''), width = 6.5, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Separately visualise connected components

tmp.graph <- igraph::make_graph(t(b.graph), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

tmp.cnt = table(tmp.comp$membership)
tmp.cnt = -sort(-tmp.cnt)
head(tmp.cnt)

   1   33   14   28  126   22 
2161   28   27   25   22   21 
k = 1
tmp.k = as.numeric(names(tmp.cnt)[k])
tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
b.graph.sub = b.graph[(b.graph[,1] %in% tmp.names) & 
                        (b.graph[,2] %in% tmp.names),]

g.part.sub.big <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names.sub.big = network.vertex.names(g.part.sub.big)


set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.type[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = g.cols) + guides(size = F)
Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'
p.big.type = p + theme(legend.position = "none")

set.seed(20)
p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F)
Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 2161 > 1' in coercion to 'logical(1)'

Save

# p.big.type
# p.big.color
# p.small.type
# p.small.color


pdf(paste(path.figures, 'graph_mob_big_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 5, height = 5)
print(p.big.type)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_big_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = 5, height = 5)
print(p.big.color)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_type.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.type)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 
pdf(paste(path.figures, 'graph_mob_small_cluster', prefix.mode[singleton.mode+1] ,'_family.pdf', sep = ''), 
    width = 6, height = 6)
print(p.small.color)     # Plot 1 --> in the first page of PDF
dev.off()
null device 
          1 

Run by accessions

path.figures.acc = '/Volumes/Samsung_T5/vienn/work_te/figures_tegraph_accessions/'
sv.bin = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_se_bin_v03.txt', stringsAsFactors = F, check.names = FALSE)
# acc = '10002'

for(acc in colnames(sv.bin)){
  sv.acc = rownames(sv.bin)[sv.bin[,acc] == 1]
  rownames(sv.se) = sv.se$gr
  sv.acc = sv.se[sv.acc, 'name']
  
  sv.acc = intersect(sv.acc, rownames(nodes))
  nodes.cnt.acc = table(nodes[sv.acc,'node'])
  
  
  sv.alpha = rep(0, length(b.graph.names))
  names(sv.alpha) = b.graph.names
  sv.alpha[names(sv.alpha) %in% names(nodes.cnt.acc)] = 1
  
  # set.seed(239)
  # p <- ggnet2(g.part, label = F, edge.color = "black", 
  #             node.size = g.nodes.cnt[b.graph.names], 
  #             color = g.nodes.fam[b.graph.names],
  #             alpha = sv.alpha,
  #             # mode = 'kamadakawai',
  #             # arrow.gap = 0, 
  #             # arrow.size = 3,
  #             palette = fam.palette) + guides(size = F) + theme(legend.position = "none")
  
  set.seed(20)
  p <- ggnet2(g.part.sub.small, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.small], 
            color = g.nodes.fam[b.graph.names.sub.small],
            alpha = sv.alpha[b.graph.names.sub.small],
            # mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_small_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()
  
  
  set.seed(20)
  p <- ggnet2(g.part.sub.big, label = F, edge.color = "black", 
            node.size = g.nodes.cnt[b.graph.names.sub.big], 
            color = g.nodes.fam[b.graph.names.sub.big],
            alpha = sv.alpha[b.graph.names.sub.big],
            mode = 'kamadakawai',
            palette = fam.palette) + guides(size = F) + theme(legend.position = "none")

  pdf(paste(path.figures.acc, 'graph_te', prefix.mode[singleton.mode+1] ,'_big_acc_',acc,'.pdf', sep = ''), width = 5, height = 5)
  print(p)     # Plot 1 --> in the first page of PDF
  dev.off()

}

p 
sv.annot = read.table('/Volumes/Samsung_T5/vienn/work_sv/svs_annotation_v03.txt', stringsAsFactors = F)
rownames(sv.annot) = sv.annot$gr
head(sv.annot)

sv.annot[extracted_values,]

Stop

stop()

Big TE-nodes

n.amount = 20

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

size.big = g.nodes.cnt[b.graph.names]
alpha.big = rep(1, length(b.graph.names))
names(alpha.big) = b.graph.names
alpha.big[size.big < n.amount] = 0

sum(size.big >= n.amount)
[1] 25
set.seed(20)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = size.big, 
            color = g.nodes.fam[b.graph.names],
            alpha= alpha.big,
            # mode = 'kamadakawai',
            # arrow.gap = 0, 
            # arrow.size = 3,
            palette = fam.palette) + guides(size = F) + guides(color = guide_legend(ncol = 2))
Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'Warning: 'length(x) = 7242 > 1' in coercion to 'logical(1)'
p

Which families specifically, and is the rate of insertion is different?

compare number of insertions with the total number of TE load


big.families = data.frame(node =  names(size.big)[size.big >= n.amount])
big.families$size = size.big[big.families$node]
big.families$fam = g.nodes.fam[big.families$node]
big.families = big.families[order(-big.families$size),]
rownames(big.families) = NULL

node.big = nodes[nodes$node %in% big.families$node,]

v = read.table(paste(path.work, 'blast_sv_on_tes.txt', sep = ''))
v = v[v$V1 %in% node.big$te,]


pos.len1 = 2
pos.len2 = 5
v1.len = sapply(unique(v$V1), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
v8.len = sapply(unique(v$V8), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len2]))
v.len = c(v1.len, v8.len)

v.sim = findNestedness(v, use.strand = F)
[1] 0
Error in `$<-.data.frame`(`*tmp*`, "overlap", value = 0) : 
  replacement has 1 row, data has 0

no-TE SV

Construct

sv.se = readRDS(paste(path.svs, 'sv_se.rds', sep = ''))
sim.cutoff = 0.85


sv.se.no.te = sv.se$name[(sv.se$te == 'noTE') & (sv.se$len > 50)]

bl.file = paste(path.work,'sv_big_on_big.txt', sep = '')
bl.sv = read.table(bl.file, stringsAsFactors = F)
bl.sv = bl.sv[bl.sv$V1 != bl.sv$V8,]

# remove having TEs
bl.sv = bl.sv[bl.sv$V1 %in% sv.se.no.te, ]
bl.sv = bl.sv[bl.sv$V8 %in% sv.se.no.te, ]

pos.len1 = 2
sv.len = sapply(unique(c(bl.sv$V1, bl.sv$V8)), function(s) as.numeric(strsplit(s,'\\|')[[1]][pos.len1]))
bl.sv$len1 = sv.len[bl.sv$V1]
bl.sv$len8 = sv.len[bl.sv$V8]
max.len = 20000
bl.sv = bl.sv[(bl.sv$len1 <= max.len) & (bl.sv$len8 <= max.len),]
bl.sv$p1 = (bl.sv$V3 - bl.sv$V2 + 1) / bl.sv$len1
bl.sv$p8 = (abs(bl.sv$V5 - bl.sv$V4) + 1) / bl.sv$len8
bl.sv$comb = as.factor(paste(bl.sv$V1, bl.sv$V8, sep = '||'))

idx.mutual = (bl.sv$p1 >= sim.cutoff) & (bl.sv$p8 >= sim.cutoff)
# There is a big discussion in my head, whether it should be '&' or '|'
# If it's not ,utual, then maybe with something else it will construct a mutual relation, 
# so we should remain for the analysis of nestedness all partial inclusions
sv.mutual = bl.sv[idx.mutual, ]
v = bl.sv[!idx.mutual, ]
v = v[!(v$comb %in% sv.mutual$comb),]

# At some point it was a step to remain only those instances which are not "unique" in combinations
# but I think it's not correct here

sv.sim = findNestedness(v, use.strand = T)
[1] 437
[1] 12
[1] 1
[1] 0
[1] 440
[1] 11
[1] 1
[1] 0
sv.sim$p1 = sv.sim$C1 / sv.len[sv.sim$V1]
sv.sim$p8 = sv.sim$C8 / sv.len[sv.sim$V8]

# here  we should finally use '|', not '&'
sv.nested = sv.sim[(sv.sim$p1 >= sim.cutoff) | (sv.sim$p8 >= sim.cutoff) ,]

# Create pre-data for defining edges
common.names = intersect(colnames(sv.mutual), colnames(sv.nested))
sv.overall = rbind(sv.mutual[,common.names], sv.nested[,common.names])
sv.overall$group = (sv.overall$p1 >= sim.cutoff) * 1 + (sv.overall$p8 >= sim.cutoff) * 2
idx1 = sv.overall$group != 2  # V1 in V8
idx2 = sv.overall$group != 1  # V8 in V1


# Edges 
sv.edges = rbind(cbind(sv.overall$V1[idx1], sv.overall$V8[idx1]),
                 cbind(sv.overall$V8[idx2], sv.overall$V1[idx2]))


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.memb = data.frame(memb = sv.graphcomp$membership)
sv.memb$name = rownames(sv.memb)
rownames(sv.memb) = NULL
rownames(sv.se) = sv.se$name
sv.memb$te = sv.se[sv.memb$name, 'te']
sv.memb$cover = sv.se[sv.memb$name, 'cover'] / sv.se[sv.memb$name, 'len']
sv.memb$len = sv.len[sv.memb$name]

Plot all

g.part <- network(sv.edges, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
b.graph.names = network.vertex.names(g.part)

set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            node.size = 1,
            # node.size = g.nodes.cnt[b.graph.names], 
            # color = g.nodes.type[b.graph.names],
            # palette = g.cols
            ) + guides(size = F)
p 

Plot with colors

p = p+ theme(legend.key.height = unit(0.5, "cm"))
p

pdf(paste(path.figures, 'graph_new_all.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

Types of the component


sv.graph <- igraph::make_graph(t(sv.edges), directed = T)
sv.graph <- igraph::simplify(sv.graph)
sv.graphcomp <- igraph::components(sv.graph)

sv.comp.member = sv.graphcomp$membership

s.tags = c("transpos","reverse","repeat","zinc", "receptor","defined prot", "undefined prot", 'no prot')
s.tags0 = rep('', length(s.tags))
s.tags0[1:4] = 'TE-like'
s.tags0[5:6] = 'Known Proteins'
s.tags0[7] = 'Undef. Proteins'
s.tags0[8] = 'No Proteins'
names(s.tags0) = s.tags

comp.tags = rep('', length(unique(sv.comp.member)))
for(s.tag in s.tags){
  tmp.tags = unique(sv.comp.member[names(g.nodes.prot)[g.nodes.prot == s.tag]])
  comp.tags[tmp.tags][comp.tags[tmp.tags] == ''] = s.tag
}
comp.tags[comp.tags == ''] = 'no prot'
comp.tags = data.frame(table(comp.tags))
colnames(comp.tags) = c('tag1', 'freq')
comp.tags$tag1 = factor(comp.tags$tag1, levels = s.tags)
comp.tags = comp.tags[order(comp.tags$tag1),]

comp.tags$tag0 = s.tags0[comp.tags$tag1]
comp.tags$tag0 = factor(comp.tags$tag0, levels = unique(s.tags0))

y.ticks = tapply(comp.tags$freq, comp.tags$tag0, sum)
y.ticks = y.ticks[!is.na(y.ticks)]

yy = sum(y.ticks) - cumsum(y.ticks) + y.ticks/2

comp.tags$ymin <- c(0, cumsum(comp.tags$freq)[-length(comp.tags$freq)])
comp.tags$ymax <- cumsum(comp.tags$freq)

x.step = rep(0, 8)
n.step = 10
x.step[c(5,7,8)] = n.step
x.step = cumsum(x.step)

comp.tags$ymin = comp.tags$ymin + x.step
comp.tags$ymax = comp.tags$ymax + x.step

y.min = tapply(comp.tags$ymin, comp.tags$tag0, min)
y.max = tapply(comp.tags$ymax, comp.tags$tag0, max)
y.val = (y.max + y.min) / 2
y.cnt = tapply(comp.tags$freq, comp.tags$tag0, sum)

df.text = data.frame(y.min = y.min, y.max = y.max, y.val = y.val, y.cnt = y.cnt, label = names(y.val))
df.text$angles <- 360 - (df.text$y.val / (max(comp.tags$ymax) + n.step)) * 360 
df.text$angles[2:3] = 180 + df.text$angles[2:3]

p = ggplot(comp.tags, aes(x = 0, y = freq, fill = tag1)) +
   geom_rect(aes(xmin = -0.5, xmax = 0.5, ymin = ymin, ymax = ymax)) +
   coord_polar("y", start = 0) +
   scale_fill_manual(values = g.cols.plus) + ylim(0, max(comp.tags$ymax) + n.step) +
   theme_void() + xlim(-1.5, 0.7) + 
   geom_text(data=df.text, aes(x = 0.7, y = y.val, label = paste(label, y.cnt, sep = ': ')), 
             angle = df.text$angles, inherit.aes = FALSE) +
  theme(legend.position="none")

p = p + annotate("text", x = -1.5, y = 0, label = paste('Total',sum(comp.tags$freq),'\n connected \ncomponents')) 

p

pdf(paste(path.figures, 'graph_new_pie_chart.pdf', sep = ''), width = 3.1, height = 3.1)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()
quartz_off_screen 
                2 

I don’t know

sv.se$freq = sv.se$freq.max
n.cutoff = 3
n = 28
sv.se$sin = 'indel'
sv.se$sin[sv.se$freq >= (n - n.cutoff)] = 'deletion'
sv.se$sin[sv.se$freq <= n.cutoff] = 'insertion'


g.nodes.prot.sin = g.nodes.prot
g.nodes.prot.sin[names(g.nodes.prot.sin) %in% sv.se$name[sv.se$sin != 'insertion'] ] = 'na'
g.cols['na'] = 'white'




set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot.sin[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 

# 
# path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
# pdf(paste(path.figures, 'graph_sv_note_insertion.pdf', sep = ''), width = 6, height = 4)
# print(p)     # Plot 1 --> in the first page of PDF
# dev.off()


alpha.edta = rep(1, length(b.graph.names))
names(alpha.edta) = b.graph.names

sv.annot.adta = rowSums(sv.annot[,11:ncol(sv.annot)] > 0.7) > 0
sv.annot.adta = sv.annot.adta[sv.se$gr]
names(sv.annot.adta) = sv.se$name
sv.annot.adta = sv.annot.adta[sv.annot.adta]
alpha.edta[names(alpha.edta) %in% names(sv.annot.adta)] = 0


set.seed(239)
p <- ggnet2(g.part, label = F, edge.color = "black", 
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            alpha=1-alpha.edta,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


pdf(paste(path.figures, 'graph_mob_note_edta.pdf', sep = ''), width = 6, height = 4)
print(p)     # Plot 1 --> in the first page of PDF
dev.off()

path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_mob_note_edta_no_legend.pdf', sep = ''), width = 5, height = 5)
print(p+ theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()

Plot with component ID



tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
tmp.graph <- igraph::simplify(tmp.graph)
tmp.comp <- igraph::components(tmp.graph)

size.limit = 5
comp.id = as.character(tmp.comp$membership)
names(comp.id) = names(tmp.comp$membership)
comp.id[tmp.comp$csize[tmp.comp$membership] < size.limit] = ''

names.te = names(g.nodes.prot)[g.nodes.prot %in% c('transpos', 'reverse')]

comp.id[!(names(comp.id) %in% names.te)] = ''

comp.id[duplicated(comp.id)] = ''


comp.remain = as.numeric(comp.id[comp.id != ''])
alpha = rep(0, length(b.graph.names))
names(alpha) = names(tmp.comp$membership)
alpha[tmp.comp$membership %in% comp.remain] = 1

set.seed(239)
p <- ggnet2(g.part, label = comp.id[b.graph.names], 
            label.color = "black",
            label.size = 3,
            edge.color = "grey", 
            alpha = alpha[b.graph.names],
            # node.size = g.nodes.cnt[b.graph.names], 
            node.size = 1,
            color = g.nodes.prot[b.graph.names],
            palette = g.cols,
            # mode = "kamadakawai"
            ) + guides(size = F)
p 


path.figures  = '/Volumes/Samsung_T5/vienn/work_te/'
pdf(paste(path.figures, 'graph_sv_note_numbers.pdf', sep = ''), width = 5, height = 5)
print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
dev.off()



# Order of components
cnt = table(tmp.comp$membership[tmp.comp$membership %in% comp.remain])
cnt = cnt[order(-cnt)]

CNV


cnv = readRDS('/Volumes/Samsung_T5/vienn/work_sv/similar_cnv_sv_on_accessions_cum_0.9.rds')

Plot one specific network


path.figures.examples  = '/Volumes/Samsung_T5/vienn/work_te/examples/'

# 
# tmp.graph <- igraph::make_graph(t(sv.edges), directed = T)
# tmp.graph <- igraph::simplify(tmp.graph)
# tmp.comp <- igraph::components(tmp.graph)
# 
# tmp.cnt = table(tmp.comp$membership)
# tmp.cnt = -sort(-tmp.cnt)

tmp.cnt = cnt

for(k in 1:length(tmp.cnt)){
  tmp.k = as.numeric(names(tmp.cnt)[k])
  tmp.names = names(tmp.comp$membership)[tmp.comp$membership == tmp.k]
  b.graph.sub = sv.edges[(sv.edges[,1] %in% tmp.names) & 
                          (sv.edges[,2] %in% tmp.names),]
  
  
  g.part.sub <- network(b.graph.sub, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
  b.graph.names.sub = network.vertex.names(g.part.sub)
  
  
    
  b.graph.size.sub <- as.numeric(sub(".*\\|", "", b.graph.names.sub))
  names(b.graph.size.sub) = b.graph.names.sub
  # b.graph.size.sub = ceiling(log(b.graph.size.sub, 10))
  
  if((length(unique( g.nodes.prot[b.graph.names.sub])) == 1)){
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.cols[g.nodes.prot[b.graph.names.sub][1]],
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  } else {
    set.seed(20)
    p <- ggnet2(g.part.sub, label = b.graph.size.sub[b.graph.names.sub], edge.color = "black", 
                node.size = 15,
                arrow.gap = 0.07, arrow.size = 3,
                color = g.nodes.prot[b.graph.names.sub],
                palette = g.cols,
                ) + guides(size = F) +  ggtitle(paste('Component #', tmp.k))
    p
  }
  
 
  
  pdf(paste(path.figures.examples, 'graph_sv_example_',k,'_comp_',tmp.k,'.pdf', sep = ''), width = 5, height = 4)
  print(p + theme(legend.position = "none"))     # Plot 1 --> in the first page of PDF
  dev.off()
  
  # annotation
  annot.tmp = sv.prot[sv.prot$name %in% b.graph.names.sub,]
  # annot.tmp = annot.tmp[annot.tmp$transpos == 1,]
  
  write.table(annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_pblast.txt', sep = ''), 
              row.names = F, col.names = F, quote = F, sep = '\t')
  
  
  # if EDTA annotation exists
  sv.tmp = unique(c(b.graph.sub))
  sv.tmp.cut <- gsub("\\|.*", "", sv.tmp)
  sv.annot.tmp = sv.annot[sv.tmp.cut,]
  n.fix = 9
  sv.annot.tmp  = sv.annot.tmp[,c(1:n.fix,n.fix+which(colSums(sv.annot.tmp[,(n.fix+1):ncol(sv.annot.tmp)]) != 0))]
  rownames(sv.annot.tmp) = sv.tmp
    
  write.table(sv.annot.tmp, paste(path.figures.examples, 'graph_sv_example_',k,'_edta.txt', sep = ''), 
             row.names = F, quote = F, sep = '\t')
  
  # Copy0Number variation
  cnv.tmp = cnv[sv.tmp,]
  
  heatmap(cnv.tmp, col = colorRampPalette(c("white", "red"))(20))
  
}

Pie-chart of proteins

library(ggplot2)

data <- data.frame(
  type = c("no proteins", "TE-related", "Категория 2", "Категория 3", "Категория 4"),
  value = c(135, 63, 85, 133)
)

pie.chart <- ggplot(data, aes(x = "", y = value, fill = type)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  theme_void()

pie.chart

Admixture groups

groups <- c(
  "germany",
  "south_sweden",
  "north_sweden",
  "south_sweden",
  "north_sweden",
  "germany",
  "western_europe",
  "central_europe",
  "italy_balkan_caucasus",
  "spain",
  "relict",
  "asia",
  "central_europe",
  "admixed",
  "spain",
  "relict",
  "italy_balkan_caucasus",
  "western_europe",
  "asia",
  "africa",
  "china",
  "china",
  "africa",
  "africa",
  "madeira",
  "madeira",
  "africa"
)

# Используем функцию table() для подсчета количества элементов в каждой группе
as.matrix(table(groups))

OLD

sunset <- colour("sunset")
discrete_rainbow <- colour("discrete rainbow")

file.te = '/Volumes/Samsung_T5/vienn/work/blast_tes_ann.txt'
sim.cutoff = 0.85
len.cutoff = 100

b = read.table(file.te, stringsAsFactors = F)
b = b[b$V1 != b$V8,]
b$len1 = as.numeric(sapply(b$V1, function(s) strsplit(s, '\\|')[[1]][7]))
b$len2 = as.numeric(sapply(b$V8, function(s) strsplit(s, '\\|')[[1]][7]))
b = b[b$len1 >= len.cutoff,]
b = b[b$len2 >= len.cutoff,]
b$comb = paste(b$V1, b$V8, sep = '^')

# Order positions in base
idx = b$V4 > b$V5
tmp = b[idx, 'V4']
b[idx, 'V4'] = b[idx, 'V5']
b[idx, 'V5'] = tmp

# --------------------------------------------------
# Get separately those, who has a unique coverage
comb.tbl = table(b$comb)
idx.uni = b$comb %in% names(comb.tbl)[comb.tbl == 1]
b.uni = b[idx.uni,]
b = b[!idx.uni,]

# This variable will be used later
b.uni$p1 = (b.uni$V3 - b.uni$V2 + 1) / b.uni$len1
b.uni$p2 = (b.uni$V5 - b.uni$V4 + 1) / b.uni$len2
b.uni = b.uni[(b.uni$p1 >= sim.cutoff) | (b.uni$p2 >= sim.cutoff),]

b.relations = data.frame(sub.te = b.uni$V1[b.uni$p1 >= sim.cutoff],
                         te = b.uni$V8[b.uni$p1 >= sim.cutoff], stringsAsFactors = F)
b.relations = rbind(b.relations,
                    data.frame(sub.te = b.uni$V8[b.uni$p2 >= sim.cutoff],
                               te = b.uni$V1[b.uni$p2 >= sim.cutoff], stringsAsFactors = F))
b.relations = unique(b.relations)

# --------------------------------------------------
# Min-max of the coverage to remove those, who are NOT in each other completely
b.cov = tapply(b$V2, b$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b$V3, b$comb, max)
b.cov$V4 = tapply(b$V4, b$comb, min)
b.cov$V5 = tapply(b$V5, b$comb, max)
b.cov$len1 = tapply(b$len1, b$comb, unique)
b.cov$len2 = tapply(b$len2, b$comb, unique)
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1) / b.cov$len1
b.cov$p2 = (b.cov$V5 - b.cov$V4 + 1) / b.cov$len2

comb.uncov = b.cov$comb[(b.cov$p1 < sim.cutoff) & (b.cov$p2 < sim.cutoff)]

b = b[!(b$comb %in% comb.uncov),]

# --------------------------------------------------
# Calculate the coverage directly for the first
b = b[order(b$V3),]
b = b[order(b$V2),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V3[-nrow(b)] > b$V3[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V2[-1] - b1$V3[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V2, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V2 = b.cov)
b.cov$V3 = tapply(b1$V3, b1$comb, max)
b.cov$len1 = tapply(b1$len1, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len1 = b.cov$len1 
b.cov$p1 = (b.cov$V3 - b.cov$V2 + 1 - b.cov$gap) / b.cov$len1
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V1,
                               te = b.cov$V8, stringsAsFactors = F))


# --------------------------------------------------
# Calculate the coverage directly for the second
b = b[order(b$V5),]
b = b[order(b$V4),]
b = b[order(b$comb),]

# Remove nested
idx = which((b$V5[-nrow(b)] > b$V5[-1]) & (b$comb[-nrow(b)] == b$comb[-1])) + 1
b1 = b[-idx,]

# Compute gaps
b1$gap = c(b1$V4[-1] - b1$V5[-nrow(b1)] - 1, 0)
b1$gap[b1$gap < 0] = 0
idx.diff.comb = which(b1$comb[-1] != b1$comb[-nrow(b1)])
b1$gap[idx.diff.comb] = 0

b.cov = tapply(b1$V4, b1$comb, min)
b.cov = data.frame(comb = names(b.cov), V4 = b.cov)
b.cov$V5 = tapply(b1$V5, b1$comb, max)
b.cov$len2 = tapply(b1$len2, b1$comb, unique)
b.cov$gap = tapply(b1$gap, b1$comb, sum)
b.cov$len2 = b.cov$len2 
b.cov$p1 = (b.cov$V5 - b.cov$V4 + 1 - b.cov$gap) / b.cov$len2
b.cov$V1 = tapply(b1$V1, b1$comb, unique)
b.cov$V8 = tapply(b1$V8, b1$comb, unique)

b.cov = b.cov[b.cov$p1 >= sim.cutoff,]


b.relations = rbind(b.relations,
                    data.frame(sub.te = b.cov$V8,
                               te = b.cov$V1, stringsAsFactors = F))

  
b.relations = unique(b.relations)


b.relations

Define clusters

b.nodes = rbind(b.relations,
                    data.frame(sub.te = b.relations$te,
                               te = b.relations$sub.te))

b.nodes$comb = paste(b.nodes$sub.te, b.nodes$te, sep = '^')

comb.tbl = table(b.nodes$comb)
comb.back.and.foth = names(comb.tbl)[comb.tbl >= 2]
b.nodes = b.nodes[b.nodes$comb %in% comb.back.and.foth,]
b.nodes = unique(b.nodes[, c('sub.te', 'te')])


te.nodes <- igraph::make_graph(t(b.nodes), directed = T)
te.nodes <- igraph::simplify(te.nodes)
te.nodes.comp <- igraph::components(te.nodes)

nodes = paste('N', te.nodes.comp$membership, sep = '')
names(nodes) = names(te.nodes.comp$membership)

Identify family for each node


nodes.family = sapply(names(nodes), function(s) strsplit(s, '\\|')[[1]][6])

nodes.family.max = tapply(nodes.family, nodes, function(s){
  tbl = table(s)
  f = names(tbl)[tbl == max(tbl)]
  if(length(f) == 1){
    return(f)
  } else {
    return('Mix')
  }
})

nodes.family.max[nodes.family.max %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
nodes.family.max[nodes.family.max %in% c('LINE/L1', 'LINE?')] = 'LINE'
nodes.family.max[nodes.family.max %in% c('Unassigned')] = 'Mix'
nodes.family.unique = unique(nodes.family.max)

Graph without singletons


b.graph.init = b.relations[(b.relations$sub.te %in% names(nodes)) & (b.relations$te %in% names(nodes)),]
b.graph = b.graph.init
b.graph = cbind(nodes[as.character(b.graph$sub.te)], nodes[as.character(b.graph$te)])
b.graph = unique(b.graph)


b.graph = b.graph[b.graph[,1] != b.graph[,2],]

# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]


# te.graph <- igraph::make_graph(t(b.graph), directed = T)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)


nodes.family.max.graph = nodes.family.max[names(nodes.family.max) %in% unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = sunset(length(unique(nodes.family.max.graph)))

graph.cols = discrete_rainbow(length(unique(nodes.family.max.graph)))
names(graph.cols) = unique(nodes.family.max.graph)
g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 1, 
            color = nodes.family.max.graph, palette = graph.cols,
            mode = "kamadakawai")# + guides(size = FALSE)
p

Graph WITH singletons



names.core = names(nodes.family.max.graph)

b.graph.init = b.relations
for(i in 1:2){
  b.graph.init[b.graph.init[,i] %in% names(nodes), i] = nodes[b.graph.init[b.graph.init[,i] %in% names(nodes), i]]
}

b.graph = unique(b.graph.init)
b.graph = b.graph[b.graph[,1] != b.graph[,2],]
b.graph = unique(b.graph)
# Verteces from the previous graph
b.graph = b.graph[(b.graph[,1] %in% names.core) | (b.graph[,2] %in% names.core),]


# reduce indirect arrows
idx.remove = c()
for(i.edge in 1:nrow(b.graph)){
  if(i.edge %% 1000 == 0) print(i.edge)
  tmp.to = b.graph[b.graph[,1] == b.graph[i.edge,1],2]
  tmp.from = b.graph[b.graph[,2] == b.graph[i.edge,2],1]
  if(length(intersect(tmp.to, tmp.from)) > 0) idx.remove = c(idx.remove, i.edge)
}
idx.remove = unique(idx.remove)
b.graph = b.graph[-idx.remove,]

te.graph <- igraph::make_graph(t(b.graph), directed = T)
d <- igraph::distances(te.graph)
# te.graph <- igraph::simplify(te.graph)
# te.graph.comp <- igraph::components(te.graph)

names.new = unique(setdiff(c(b.graph[,1], b.graph[,2]), names(nodes.family.max)))
# names.new.val = paste('G',1:length(names.new), sep = '')
# names(names.new.val) = names.new
# names.new.val = 

names.new.family = sapply(names.new, function(s) strsplit(s, '\\|')[[1]][6])
names.new.family[names.new.family %in% c('DNA/Pogo', 'DNA/Tc1', 'DNA/Harbinger', 'DNA/En-Spm',
                     'DNA/HAT', 'DNA', 'DNA/Mariner')] = 'DNA'
names.new.family[names.new.family %in% c('RathE1_cons', 'RathE2_cons')] = 'DNA'
names.new.family[names.new.family %in% c('LINE/L1', 'LINE?')] = 'LINE'
names.new.family[names.new.family %in% c('Unassigned')] = 'Mix'


nodes.family.max.add = c(nodes.family.max, names.new.family)
nodes.family.max.add = nodes.family.max.add[unique(c(b.graph[,1], b.graph[,2]))]

graph.cols = discrete_rainbow(length(unique(nodes.family.max.add)))
graph.cols = sample(graph.cols)
names(graph.cols) = unique(nodes.family.max.add)

g.part <- network(b.graph, matrix.type = "edgelist", ignore.eval = FALSE, directed = TRUE)
p <- ggnet2(g.part, label = FALSE, edge.color = "black", node.size = 0.5, 
            color = nodes.family.max.add,
            palette = graph.cols, mode = "kamadakawai")
p

TSNE



library(Rtsne)




d <- igraph::distances(te.graph)
d.max = max(d[!is.infinite(d)])

d[is.infinite(d)] = d.max * 1.3

tSNE <- Rtsne(d, is_distance = TRUE, dims = 2)

plot(tSNE$Y[,1], tSNE$Y[,2])
LS0tCnRpdGxlOiAiR3JhcGggb2YgVEVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKIyBTZXR1cApgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyBsaWJyYXJ5KGdncGxvdDIpCiMgbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShnZ3BhdHRlcm4pCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShjb2xvclJhbXBzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KCdpZ3JhcGgnKQpsaWJyYXJ5KGdnbmV0KQpsaWJyYXJ5KG5ldHdvcmspCmxpYnJhcnkoa2hyb21hKQpsaWJyYXJ5KGRwbHlyKQoKc291cmNlKCdzaW1pbGFyaXR5LlInKQoKc3Vuc2V0IDwtIGNvbG91cigic3Vuc2V0IikKZGlzY3JldGVfcmFpbmJvdyA8LSBjb2xvdXIoImRpc2NyZXRlIHJhaW5ib3ciKQoKcGF0aC5iYXNlID0gJy4uLy4uLy4uLycKcGF0aC53b3JrID0gcGFzdGUocGF0aC5iYXNlLCAnMDJfYW5hbHlzaXMvMDRfc3YvMDFfZGF0YS8nLCBzZXAgPSAnJykKcGF0aC50YWlyID0gcGFzdGUocGF0aC5iYXNlLCAnMDFfZGF0YV9jb21tb24vMDFfdGFpcjEwLycsIHNlcCA9ICcnKQpwYXRoLmZpZ3VyZXMgPSBwYXN0ZShwYXRoLmJhc2UsICcwMl9hbmFseXNpcy8wNF9zdi8wM19maWd1cmVzLycsIHNlcCA9ICcnKQpwYXRoLnN2cyA9IHBhc3RlKHBhdGguYmFzZSwgJzAxX2RhdGFfY29tbW9uLzAyX2Fubm90X2Rlbm92by8wMl9wYW5uYWdyYW0vc3ZzLycsIHNlcCA9ICcnKQojIHBhdGguZ2VuZXMgPSBwYXN0ZShwYXRoLmJhc2UsICcwMV9kYXRhX2NvbW1vbi8wMl9hbm5vdF9kZW5vdm8vMDJfcGFubmFncmFtL2dlbmVzLycsIHNlcCA9ICcnKQoKIyBzaW0uY3V0b2ZmID0gMC45CgpzaW0uY3V0b2ZmID0gMC44NQoKYGBgCgojIENvb2xvcnMKYGBge3J9CgoKZmFtLnBhbGV0dGUgPSBjKCkKZmFtLnBhbGV0dGVbJ1VuYXNzaWduZWQnXSA9ICdncmV5JwpmYW0ucGFsZXR0ZVsnTWl4J10gPSAnZ3JleTIwJwpmYW0ucGFsZXR0ZVsnTWl4IHdpdGggSGVsaXRyb24nXSA9ICcjMjY2RDk4JwpmYW0ucGFsZXR0ZVsnSGVsaXRyb24nXSA9ICcjQkNBQ0RFJwpmYW0ucGFsZXR0ZVsiTFRSL0NvcGlhIl0gPSAnI0JGREIzOCcKZmFtLnBhbGV0dGVbIkxUUi9HeXBzeSJdID0gJyM1NEI0MzUnCmZhbS5wYWxldHRlWyJETkEvSEFUIl0gPSAnI0Y5QjVEMCcKZmFtLnBhbGV0dGVbIkROQSsiXSA9ICcjQzg2NThDJwpmYW0ucGFsZXR0ZVsiRE5BL011RFIiXSA9ICcjOTcxNTQ5JwoKCmZhbS5wYWxldHRlWyJMSU5FIl0gPSAnI0ZGQzI2RicKZmFtLnBhbGV0dGVbIlJhdGhFMS8yLzNfY29ucyJdID0gJyNDMzgxNTQnCmZhbS5wYWxldHRlWyJTSU5FIl0gPSAnIzg4NEEzOScKZmFtLnBhbGV0dGVbIlRFRyJdID0gJyM0RTM2MzYnCgpgYGAKCgoKIyBURXMKYGBge3J9CgojIExvYWQgc2ltaWxhcml0eSBmdW5jdGlvbgoKYmwuZmlsZSA9IHBhc3RlKHBhdGgud29yaywnbmV3X3RlX29uX3RlLmZhc3RhJyxzZXAgPSAnJykKYmwucmVzID0gcmVhZC50YWJsZShibC5maWxlKQpibC5yZXMgPSBibC5yZXNbYmwucmVzJFYxICE9IGJsLnJlcyRWOCxdCgpibC5yZXMuaW5pdCA9IGJsLnJlcwpibC5yZXMgPSBibC5yZXNbYmwucmVzJFY2ID49IHNpbS5jdXRvZmYgKiAxMDAsXQoKcmVzLm5lc3QgPSBmaW5kTmVzdGVkbmVzcyhibC5yZXMsIHVzZS5zdHJhbmQgPSBGKQoKcmVzLm5lc3QubGVuID0gc2FwcGx5KHVuaXF1ZShjKHJlcy5uZXN0JFYxLCByZXMubmVzdCRWOCkpLCBmdW5jdGlvbihzKSBhcy5udW1lcmljKHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzVdKSkKICAKcmVzLm5lc3QkbGVuMSA9IHJlcy5uZXN0LmxlbltyZXMubmVzdCRWMV0KcmVzLm5lc3QkbGVuOCA9IHJlcy5uZXN0LmxlbltyZXMubmVzdCRWOF0KcmVzLm5lc3QkcDEgPSByZXMubmVzdCRDMSAvIHJlcy5uZXN0JGxlbjEKcmVzLm5lc3QkcDggPSByZXMubmVzdCRDOCAvIHJlcy5uZXN0JGxlbjgKCnJlcy5uZXN0LnNpbSA9IHJlcy5uZXN0WyhyZXMubmVzdCRwMSA+PSBzaW0uY3V0b2ZmKSB8IAogICAgICAgICAgICAgICAgICAgICAgICAgIChyZXMubmVzdCRwOCA+PSBzaW0uY3V0b2ZmKSxdCmBgYAoKIyMgSG93IG1hbnkgVEVzIGFyZSBpbiB0aGUgZ3JhcGgKRGlzdHJpYnV0aW9uIGFtb25nIGZhbWlsaWVzIGFuZCBzdWJmYW1pbGllcwpEaXN0cmlidXRpb24gYW1vbmcgbGVuZ3RocwpgYGB7cn0KdGUuaW4uZ3JhcGggPSB1bmlxdWUoYyhyZXMubmVzdCRWMSwgcmVzLm5lc3QkVjgpKQoKIyBXaGF0IGlzIHRoZSBhY3R1YWwgbnVtYmVyIG9mIFRFcwpmaWxlLmNvbnRlbnQgPC0gcmVhZExpbmVzKGJsLmZpbGUpCgpzZWxlY3RlZC5saW5lcyA8LSBmaWxlLmNvbnRlbnRbZ3JlcGwoIl4jIFF1ZXJ5OnxoaXRzIGZvdW5kIiwgZmlsZS5jb250ZW50KV0KZGYucXVlcnkgPSBkYXRhLmZyYW1lKGIucXVlcnk9c2VsZWN0ZWQubGluZXNbc2VxKDEsIGxlbmd0aChzZWxlY3RlZC5saW5lcyksIGJ5ID0gMildLAogICAgICAgICAgICAgICAgICAgICAgYi5oaXRzPXNlbGVjdGVkLmxpbmVzW3NlcSgyLCBsZW5ndGgoc2VsZWN0ZWQubGluZXMpLCBieSA9IDIpXSkKCmRmLnF1ZXJ5JHF1ZXJ5ICA8LSBnc3ViKCJeIyBRdWVyeTogKC4qKSIsICJcXDEiLCBkZi5xdWVyeSRiLnF1ZXJ5KQpkZi5xdWVyeSRsZW4gPC0gYXMubnVtZXJpYyhzYXBwbHkoc3Ryc3BsaXQoZGYucXVlcnkkcXVlcnksICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs1XSkpCmRmLnF1ZXJ5JGhpdHMgPC0gYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfZXh0cmFjdChkZi5xdWVyeSRiLmhpdHMsICJcXGQrIikpCmRmLnF1ZXJ5JHZhbC5oaXRzID0gZGYucXVlcnkkaGl0cwpkZi5xdWVyeSR2YWwuaGl0c1tkZi5xdWVyeSR2YWwuaGl0cyA+PSAyXSA9IDIKZGYucXVlcnkkdmFsLmhpdHNbZGYucXVlcnkkcXVlcnkgJWluJSBibC5yZXMkVjhdID0gMgpkZi5xdWVyeSR2YWwuaGl0c1tkZi5xdWVyeSRxdWVyeSAlaW4lIHRlLmluLmdyYXBoXSA9IDMKaGl0LnZhbHVlcyA9IGMoJzAgaGl0cycsICcxIHNlbGYtaGl0JywgJ3BhcnRpYWwgb3ZlcmxhcCcsICdpbiBncmFwaCcsICJpbiBncmFwaCBidXQgbm90IGluIFNWcyIpCmRmLnF1ZXJ5JHMuaGl0cyA9IGhpdC52YWx1ZXNbZGYucXVlcnkkdmFsLmhpdHMrMV0KZGYucXVlcnkkcy5oaXRzID0gZmFjdG9yKGRmLnF1ZXJ5JHMuaGl0cywgbGV2ZWxzID0gcmV2KGhpdC52YWx1ZXMpKQpkZi5xdWVyeSRmYW1pbHkgPC0gc2FwcGx5KHN0cnNwbGl0KGRmLnF1ZXJ5JHF1ZXJ5LCAiXFx8IiksIGZ1bmN0aW9uKHgpIHhbOV0pCmRmLnF1ZXJ5JHN1YmZhbSA8LSBzYXBwbHkoc3Ryc3BsaXQoZGYucXVlcnkkcXVlcnksICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs4XSkKCgpteV9jb2xvcnMgPC0gY29sb3JzIDwtIGMoImluIGdyYXBoIiA9ICIjNjc2RkEzIiwKICAgICAgICAgICAgInBhcnRpYWwgb3ZlcmxhcCIgPSAiI0ZGOUYyOSIsCiAgICAgICAgICAgICIxIHNlbGYtaGl0IiA9ICIjNkVCRjhCIiwKICAgICAgICAgICAgIjAgaGl0cyIgPSAiI0Q4MjE0OCIsCiAgICAgICAgICAgICJpbiBncmFwaCBidXQgbm90IGluIFNWcyIgPSAiIzE1MUQzQiIpCgoKIyBURXMsIHdoaWNoIGFyZSBub3QgaW4gU1ZzCnRlLmluLnN2cyA9IHJlYWQudGFibGUocGFzdGUocGF0aC53b3JrLCAnYmxhc3RfdGVzX29uX3N2LnR4dCcsIHNlcCA9ICcnKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnRlLnJlc3QgPSBzZXRkaWZmKGRmLnF1ZXJ5JHF1ZXJ5LCB0ZS5pbi5zdnMkVjEpCnRlLmluLnN2cyA9IHJlYWQudGFibGUocGFzdGUocGF0aC53b3JrLCAnYmxhc3Rfc3Zfb25fdGVzLnR4dCcsIHNlcCA9ICcnKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnRlLnJlc3QgPSBzZXRkaWZmKHRlLnJlc3QsIHRlLmluLnN2cyRWOCkKZGYucXVlcnkkcy5oaXRzW2RmLnF1ZXJ5JHF1ZXJ5ICVpbiUgdGUucmVzdF0gPSAiaW4gZ3JhcGggYnV0IG5vdCBpbiBTVnMiCgoKcCA9IGdncGxvdChkZi5xdWVyeSwgYWVzKHggPSBsZW4sIGZpbGwgPSBzLmhpdHMsIGNvbG9yID0gcy5oaXRzKSkgKwogICMgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGFscGhhPTAuNSwgY29sb3IgPSAiYmxhY2siLCBiaW5zID0gMzApICsKICAjIGdlb21faml0dGVyKGhlaWdodCA9IDAuMDIsIHdpZHRoID0gMCwgYWxwaGEgPSAwLjcpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXlfY29sb3JzKSArCiAgIHNjYWxlX3hfbG9nMTAoKSArCiAgbGFicyhmaWxsID0gTlVMTCwgY29sb3IgPSBOVUxMKSArCiAgeGxhYignbGVuZ3RoIG9mIFRFcycpICsgeWxhYignTm9ybWFsaXNlZCBkZW5zaXR5JykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygxLCAxKSwgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDEpLAogICAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiZ3JleTkwIikpCgpwCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfYmxhc3RfbGVuX2RlbnNpdHkucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCnRhYmxlKGRmLnF1ZXJ5JHZhbC5oaXRzKQpgYGAKCgojIyMgVEVzIG5vdCBpbiBTVnMKYGBge3J9CiMgVEVzIGluIHRlLWdyYXBoOiB0ZS5pbi5ncmFwaAojIFRFcyB3aGljaCBhcmUgaGF2ZSBubyBjb25uZWN0aW9uIHRvIFNWcwoKZGYgPSBhcy5kYXRhLmZyYW1lKHRhYmxlKGRmLnF1ZXJ5JHMuaGl0cykpCgogIApjb2xvcnMgPC0gYygiaW4gZ3JhcGgiID0gIiM2NzZGQTMiLAogICAgICAgICAgICAicGFydGlhbCBvdmVybGFwIiA9ICIjRkY5RjI5IiwKICAgICAgICAgICAgIjEgc2VsZi1oaXQiID0gIiM2RUJGOEIiLAogICAgICAgICAgICAiMCBoaXRzIiA9ICIjRDgyMTQ4IiwKICAgICAgICAgICAgImluIGdyYXBoIGJ1dCBub3QgaW4gU1ZzIiA9ICIjMTUxRDNCIikKCgpwID0gZ2dwbG90KGRmLCBhZXMoeCA9ICIiLCB5ID0gRnJlcSwgZmlsbCA9IFZhcjEpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0xLCBhbHBoYSA9IDAuNykgKwogIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQ9MCkgKwogIGxhYnModGl0bGU9TlVMTCwgZmlsbD0iQ2F0ZWdvcmllcyIpICsKICB0aGVtZV92b2lkKCkrCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSx4ID0gMS4zKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICd0ZXNfc2VsZl9ibGFzdF9waWVfY2hhcnQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDMsIGhlaWdodCA9IDMpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCgoKCmBgYAoKIyMgRXhhbXBsZXMKIyMjIEV4YW1wbGVzIG5vIGhpdHMKYGBge3J9CgoKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHZhbC5oaXRzID09ICcwJyksXQoKY250LmluaXQgPSBjKHRhYmxlKGRmLnF1ZXJ5JGZhbWlseSkpCmNudC50bXAgPSBjKHRhYmxlKGRmLnF1ZXJ5LnRtcCRmYW1pbHkpKQoKY29tbW9uX25hbWVzIDwtIGludGVyc2VjdChuYW1lcyhjbnQuaW5pdCksIG5hbWVzKGNudC50bXApKQojINCh0L7Qt9C00LDQvdC40LUgZGF0YWZyYW1lINGC0L7Qu9GM0LrQviDQtNC70Y8g0YHQvtCy0L/QsNC00LDRjtGJ0LjRhSDQuNC80LXQvQpkZl9tYXRjaCA8LSBkYXRhLmZyYW1lKG5hbWVzID0gY29tbW9uX25hbWVzLCB2YWx1ZXMuaW5pdCA9IGNudC5pbml0W2NvbW1vbl9uYW1lc10sIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcy50bXAgPSBjbnQudG1wW2NvbW1vbl9uYW1lc10pCgoKZ3JhZGllbnRfY29sb3JzIDwtIGMoZGlzY3JldGVfcmFpbmJvdyhucm93KGRmX21hdGNoKSkpCm5hbWVzKGdyYWRpZW50X2NvbG9ycykgPSBOVUxMCgoKcCA9IGdncGxvdChkZl9tYXRjaCwgYWVzKHggPSB2YWx1ZXMuaW5pdCwgeSA9IHZhbHVlcy50bXAsIGxhYmVsID0gbmFtZXMsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIGdlb21fdGV4dChoanVzdCA9IDAsIHZqdXN0ID0gMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoImNvdW50cyBvZiBObyBoaXRzIikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ3JhZGllbnRfY29sb3JzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkKcAoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICd0ZXNfc2VsZl9zY2F0dGVyX25vX2hpdHMucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKIyBObyBoaXRzIGFuZCBsb25nCmxlbi5taW4gPSAxMDAKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHZhbC5oaXRzID09ICcwJykgJiAoZGYucXVlcnkkbGVuID49IGxlbi5taW4pLF0KCgpjbnQuaW5pdCA9IGModGFibGUoZGYucXVlcnkkZmFtaWx5KSkKY250LnRtcCA9IGModGFibGUoZGYucXVlcnkudG1wJGZhbWlseSkpCgpjb21tb25fbmFtZXMgPC0gaW50ZXJzZWN0KG5hbWVzKGNudC5pbml0KSwgbmFtZXMoY250LnRtcCkpCiMg0KHQvtC30LTQsNC90LjQtSBkYXRhZnJhbWUg0YLQvtC70YzQutC+INC00LvRjyDRgdC+0LLQv9Cw0LTQsNGO0YnQuNGFINC40LzQtdC9CmRmX21hdGNoIDwtIGRhdGEuZnJhbWUobmFtZXMgPSBjb21tb25fbmFtZXMsIHZhbHVlcy5pbml0ID0gY250LmluaXRbY29tbW9uX25hbWVzXSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzLnRtcCA9IGNudC50bXBbY29tbW9uX25hbWVzXSkKCgpncmFkaWVudF9jb2xvcnMgPC0gYyhkaXNjcmV0ZV9yYWluYm93KG5yb3coZGZfbWF0Y2gpKSkKbmFtZXMoZ3JhZGllbnRfY29sb3JzKSA9IE5VTEwKCnAgPSBnZ3Bsb3QoZGZfbWF0Y2gsIGFlcyh4ID0gdmFsdWVzLmluaXQsIHkgPSB2YWx1ZXMudG1wLCBsYWJlbCA9IG5hbWVzLCBjb2xvciA9IG5hbWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgIyBnZW9tX3RleHQoaGp1c3QgPSAwLCB2anVzdCA9IDApICsKICAjIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBwYXN0ZShuYW1lcywnKCcsdmFsdWVzLnRtcCwnKScsc2VwID0nJykpLCBtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoImNvdW50cyBvZiBObyBoaXRzIikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ3JhZGllbnRfY29sb3JzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdlb21fdGV4dChhZXMoeD0wLHk9SW5mLGhqdXN0PTAsIHZqdXN0PTMsCiAgICAgICAgICAgICAgICBsYWJlbD1wYXN0ZSgnTGVuZ3RoID49JywgbGVuLm1pbikpLCBjb2xvciA9ICdncmV5MjAnKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX3NjYXR0ZXJfbm9faGl0c19sb25nLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgpgYGAKCmBgYHtyfQoKaGVhZChkZi5xdWVyeS50bXBbZGYucXVlcnkudG1wJGZhbWlseSA9PSAnRE5BL011RFInLF0kcXVlcnkpCmBgYAoKCiMjIyBFeGFtcGxlcyBvbmUgc2VsZi1oaXQKIyMjIyBmYW1pbGllcwpgYGB7cn0KCgpkZi5xdWVyeS50bXAgPSBkZi5xdWVyeVsoZGYucXVlcnkkdmFsLmhpdHMgPT0gMSksXQoKY250LmluaXQgPSBjKHRhYmxlKGRmLnF1ZXJ5JGZhbWlseSkpCmNudC50bXAgPSBjKHRhYmxlKGRmLnF1ZXJ5LnRtcCRmYW1pbHkpKQoKY29tbW9uX25hbWVzIDwtIGludGVyc2VjdChuYW1lcyhjbnQuaW5pdCksIG5hbWVzKGNudC50bXApKQojINCh0L7Qt9C00LDQvdC40LUgZGF0YWZyYW1lINGC0L7Qu9GM0LrQviDQtNC70Y8g0YHQvtCy0L/QsNC00LDRjtGJ0LjRhSDQuNC80LXQvQpkZl9tYXRjaCA8LSBkYXRhLmZyYW1lKG5hbWVzID0gY29tbW9uX25hbWVzLCB2YWx1ZXMuaW5pdCA9IGNudC5pbml0W2NvbW1vbl9uYW1lc10sIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcy50bXAgPSBjbnQudG1wW2NvbW1vbl9uYW1lc10pCgoKZ3JhZGllbnRfY29sb3JzIDwtIGMoZGlzY3JldGVfcmFpbmJvdyhucm93KGRmX21hdGNoKSkpCm5hbWVzKGdyYWRpZW50X2NvbG9ycykgPSBOVUxMCgoKcCA9IGdncGxvdChkZl9tYXRjaCwgYWVzKHggPSB2YWx1ZXMuaW5pdCwgeSA9IHZhbHVlcy50bXAsIGxhYmVsID0gbmFtZXMsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIGdlb21fdGV4dChoanVzdCA9IDAsIHZqdXN0ID0gMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoIkNvdW50cyBpbiBcIjEgc2VsZi1oaXRzXCIgY2F0ZWdvcnkiKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBncmFkaWVudF9jb2xvcnMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3Rlc19zZWxmX3NjYXR0ZXJfMV9zZWxmaGl0c19mYW0ucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQpgYGAKCiMjIyMgc3ViZmFtaWxpZXMKYGBge3J9CgoKZGYucXVlcnkudG1wID0gZGYucXVlcnlbKGRmLnF1ZXJ5JHZhbC5oaXRzID09IDEpICYgKGRmLnF1ZXJ5JGxlbiA+PSA2MDApLF0KCmNudC5pbml0ID0gYyh0YWJsZShkZi5xdWVyeSRzdWJmYW0pKQpjbnQudG1wID0gYyh0YWJsZShkZi5xdWVyeS50bXAkc3ViZmFtKSkKCmNvbW1vbl9uYW1lcyA8LSBpbnRlcnNlY3QobmFtZXMoY250LmluaXQpLCBuYW1lcyhjbnQudG1wKSkKIyDQodC+0LfQtNCw0L3QuNC1IGRhdGFmcmFtZSDRgtC+0LvRjNC60L4g0LTQu9GPINGB0L7QstC/0LDQtNCw0Y7RidC40YUg0LjQvNC10L0KZGZfbWF0Y2ggPC0gZGF0YS5mcmFtZShuYW1lcyA9IGNvbW1vbl9uYW1lcywgdmFsdWVzLmluaXQgPSBjbnQuaW5pdFtjb21tb25fbmFtZXNdLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMudG1wID0gY250LnRtcFtjb21tb25fbmFtZXNdKQoKCiMgZ3JhZGllbnRfY29sb3JzIDwtIGMoZGlzY3JldGVfcmFpbmJvdyhucm93KGRmX21hdGNoKSkpCm5hbWVzKGdyYWRpZW50X2NvbG9ycykgPSBOVUxMCgoKcCA9IGdncGxvdChkZl9tYXRjaCwgYWVzKHggPSB2YWx1ZXMuaW5pdCwgeSA9IHZhbHVlcy50bXAsIGxhYmVsID0gbmFtZXMsIGNvbG9yID0gbmFtZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIGdlb21fdGV4dChoanVzdCA9IDAsIHZqdXN0ID0gMCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKwogIHhsYWIoIkluaXRpYWwgY291bnRzIikgKwogIHlsYWIoIkNvdW50cyBpbiBcIjEgc2VsZi1oaXRzXCIgY2F0ZWdvcnkiKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogICMgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGdyYWRpZW50X2NvbG9ycykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgdGhlbWVfbWluaW1hbCgpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAndGVzX3NlbGZfc2NhdHRlcl8xX3NlbGZoaXRzX3N1YmZhbS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNywgaGVpZ2h0ID0gNSkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKYGBgCgojIyMjIGluZGl2aWR1YWxzIGZyb20gc3ViZmFtaWxpZXMKYGBge3J9CnMuc3ViZmFtID0gJ0FUUkVQOCcKCmRmLnF1ZXJ5LnRtcCA9IGRmLnF1ZXJ5WyhkZi5xdWVyeSRzdWJmYW0gPT0gcy5zdWJmYW0pICYgKGRmLnF1ZXJ5JGxlbiA+PSA2MDApLF0KZGYucXVlcnkudG1wCgoKYGBgCgoKCgoKIyMgQ3JlYXRpbmcgdGhlIGdyYXBoCmBgYHtyfQojIGFsbCBlZGdlcwppZHggPSByZXMubmVzdCRwMSA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gY2JpbmQocmVzLm5lc3QkVjFbaWR4XSwgcmVzLm5lc3QkVjhbaWR4XSkKaWR4ID0gcmVzLm5lc3QkcDggPj0gc2ltLmN1dG9mZgplZGdlcyA9IHJiaW5kKGVkZ2VzLCBjYmluZChyZXMubmVzdCRWOFtpZHhdLCByZXMubmVzdCRWMVtpZHhdKSkKdGUuZW5nZXMubmFtZXMgPSB1bmlxdWUoYyhlZGdlc1ssMV0sIGVkZ2VzWywyXSkpCnRlLmVuZ2VzLmZhbSA9IHNhcHBseSh0ZS5lbmdlcy5uYW1lcywgZnVuY3Rpb24ocykgc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bOV0gKQoKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ0ROQS9Qb2dvJywgJ0ROQS9UYzEnLCAnRE5BL0hhcmJpbmdlcicsICdETkEvRW4tU3BtJywKICAgICAgICAgICAgICAgICAgICAgJ0ROQS9IQVQnLCAnRE5BJywgJ0ROQS9NYXJpbmVyJyldID0gJ0ROQScKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1JhdGhFMV9jb25zJywgJ1JhdGhFMl9jb25zJywgJ1JhdGhFM19jb25zJyldID0gJ1JhdGhFMS8yLzNfY29ucycKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ0xJTkUvTDEnLCAnTElORT8nKV0gPSAnTElORScKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1VuYXNzaWduZWQnKV0gPSAnTWl4Jwp0ZS5lbmdlcy5mYW1bdGUuZW5nZXMuZmFtICVpbiUgYygnUkMvSGVsaXRyb24nKV0gPSAnSGVsaXRyb24nCgplZGdlcyA9IGVkZ2VzW3RlLmVuZ2VzLmZhbVtlZGdlc1ssMV1dICE9ICdURUcnLF0KZWRnZXMgPSBlZGdlc1t0ZS5lbmdlcy5mYW1bZWRnZXNbLDJdXSAhPSAnVEVHJyxdCnRlLmVuZ2VzLm5hbWVzID0gdW5pcXVlKGMoZWRnZXNbLDFdLCBlZGdlc1ssMl0pKQoKCiMgbm9kZXMKaWR4ID0gKHJlcy5uZXN0JHAxID49IHNpbS5jdXRvZmYpICYgKHJlcy5uZXN0JHA4ID49IHNpbS5jdXRvZmYpCnRlLm5vZGVzID0gY2JpbmQocmVzLm5lc3QkVjFbaWR4XSwgcmVzLm5lc3QkVjhbaWR4XSkKdGUubm9kZXMgPSB0ZS5ub2Rlc1t0ZS5lbmdlcy5mYW1bdGUubm9kZXNbLDFdXSAhPSAnVEVHJyxdCnRlLm5vZGVzID0gdGUubm9kZXNbdGUuZW5nZXMuZmFtW3RlLm5vZGVzWywyXV0gIT0gJ1RFRycsXQoKdGUucmVzdCA9IHNldGRpZmYodGUuZW5nZXMubmFtZXMsIGModGUubm9kZXNbLDFdLCB0ZS5ub2Rlc1ssMl0pKQoKCnRlLm5vZGVzLmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KHRlLm5vZGVzKSwgZGlyZWN0ZWQgPSBUKQp0ZS5ub2Rlcy5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHRlLm5vZGVzLmdyYXBoKQp0ZS5ub2Rlcy5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0ZS5ub2Rlcy5ncmFwaCkKCm5vZGVzID0gZGF0YS5mcmFtZShub2RlID0gcGFzdGUoJ04nLCB0ZS5ub2Rlcy5jb21wJG1lbWJlcnNoaXAsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICAgICAgICB0ZSA9IG5hbWVzKHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCkpCgpub2Rlcy5yZXN0ID0gZGF0YS5mcmFtZShub2RlID0gcGFzdGUoJ1InLCAoMTpsZW5ndGgodGUucmVzdCkpLCBzZXAgPSAnJyksIHRlID0gdGUucmVzdCkKbm9kZXMgPSByYmluZChub2Rlcywgbm9kZXMucmVzdCkKCnJvd25hbWVzKG5vZGVzKSA9IG5vZGVzJHRlCgoKbm9kZXMuY250ID0gZGF0YS5mcmFtZShjbnQgPSBjKHRhYmxlKG5vZGVzJG5vZGUpKSkKbm9kZXMuY250JG5vZGUgPSByb3duYW1lcyhub2Rlcy5jbnQpCm5vZGVzLmNudCRmYW0gPSBzYXBwbHkobm9kZXMuY250JG5vZGUsIGZ1bmN0aW9uKHMpewogIHMudGUgPSBub2RlcyR0ZVtub2RlcyRub2RlID09IHNdCiAgZmFtLnRlID0gdW5pcXVlKHRlLmVuZ2VzLmZhbVtzLnRlXSkKICBpZihsZW5ndGgoZmFtLnRlKSA9PSAxKXsKICAgIHJldHVybihmYW0udGUpCiAgfSBlbHNlIHsKICAgIGZhbS50ZSA9IHNldGRpZmYoZmFtLnRlLCAnVEVHJykKICAgIGlmKGxlbmd0aChmYW0udGUpID09IDEpIHJldHVybihmYW0udGUpCiAgICByZXR1cm4oJ01peCcpCiAgfQp9KQp0YWJsZShub2Rlcy5jbnQkZmFtKQoKCiMgUmVkZWZpbmUgZWRnZXMgYnV0IHdpdGggbm9kZSBuYW1lcwppZHguZW5kZXMgPSAoZWRnZXNbLDFdICVpbiUgbm9kZXMkdGUpICYgKGVkZ2VzWywyXSAlaW4lIG5vZGVzJHRlKQpiLmdyYXBoID0gY2JpbmQobm9kZXNbZWRnZXNbaWR4LmVuZGVzLDFdLCAnbm9kZSddLG5vZGVzW2VkZ2VzW2lkeC5lbmRlcywyXSwgJ25vZGUnXSkKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQojIGIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaC51bmkgPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaCA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gIT0gYi5ncmFwaFssMl0sXQoKbGVuZ3RoKHVuaXF1ZShjKGIuZ3JhcGhbLDFdLCBiLmdyYXBoWywyXSkpKQoKIyByZWR1Y2UgaW5kaXJlY3QgYXJyb3dzCmlkeC5yZW1vdmUgPSBjKCkKZm9yKGkuZWRnZSBpbiAxOm5yb3coYi5ncmFwaCkpewogIGlmKGkuZWRnZSAlJSAxMDAwID09IDApIHByaW50KGkuZWRnZSkKICB0bXAudG8gPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbaS5lZGdlLDFdLDJdCiAgdG1wLmZyb20gPSBiLmdyYXBoW2IuZ3JhcGhbLDJdID09IGIuZ3JhcGhbaS5lZGdlLDJdLDFdCiAgaWYobGVuZ3RoKGludGVyc2VjdCh0bXAudG8sIHRtcC5mcm9tKSkgPiAwKSBpZHgucmVtb3ZlID0gYyhpZHgucmVtb3ZlLCBpLmVkZ2UpCn0KaWR4LnJlbW92ZSA9IHVuaXF1ZShpZHgucmVtb3ZlKQpiLmdyYXBoID0gYi5ncmFwaFstaWR4LnJlbW92ZSxdCiMgYi5ncmFwaCA9IHJiaW5kKGIuZ3JhcGgsIGIuZ3JhcGgudW5pKQoKIyBQcmludCBncmFwaAoKZy5ub2Rlcy5mYW0gPSBub2Rlcy5jbnQkZmFtCm5hbWVzKGcubm9kZXMuZmFtKSA9IG5vZGVzLmNudCRub2RlCmcubm9kZXMuY250ID0gbm9kZXMuY250JGNudApuYW1lcyhnLm5vZGVzLmNudCkgPSBub2Rlcy5jbnQkbm9kZQoKZy5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKGcubm9kZXMuZmFtKSkpCm5hbWVzKGcuY29scykgPSB1bmlxdWUoZy5ub2Rlcy5mYW0pCgpiLmdyYXBoLmluaXQgPSBiLmdyYXBoCgoKZy5wYXJ0IDwtIG5ldHdvcmsoYi5ncmFwaCwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCmBgYAoKIyMjIE9sZCBjb2xvcnMKYGBge3J9CiMgcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKIyAgICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiMgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzXSwKIyAgICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAojICAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKIyAgICAgICAgICAgICApIAojIHAgKyBndWlkZXMoc2l6ZSA9IEYpCiMgCiMgIyAKIyAjIGIuZ3JhcGguZmFtID0gY2JpbmQoZy5ub2Rlcy5mYW1bYi5ncmFwaFssMV1dLCBnLm5vZGVzLmZhbVtiLmdyYXBoWywyXV0pCiMgIyBiLmdyYXBoLmZhbQojICMgCiMgIyB3aGljaCgoYi5ncmFwaC5mYW1bLDFdID09ICdETkEvTXVEUicpICYgKGIuZ3JhcGguZmFtWywxXSA9PSAnTElORScpKQojIAoKCmBgYAoKIyMjIE5ldyBGYW1pbHkgY29sb3JzCmBgYHtyfQpnLmZhbS5uYW1lcyA9IHNvcnQodW5pcXVlKGcubm9kZXMuZmFtKSkKZmFtLnBhbGV0dGUgPSBjKCkKaWR4LnBhbGxldGUgPSBjKCkKCmlkeC5mYW0gPC0gZ3JlcCgiXkhlbGl0cm9uIiwgZy5mYW0ubmFtZXMsIHZhbHVlID0gRkFMU0UpCnRtcC5wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygnI0JGQUNFMicsICcjMjY2RDk4JywgJyM0MjJCNzInKSkobGVuZ3RoKGlkeC5mYW0pKQppZHgucGFsbGV0ZSA9IGMoaWR4LnBhbGxldGUsIGlkeC5mYW0pCmZhbS5wYWxldHRlID0gYyhmYW0ucGFsZXR0ZSwgdG1wLnBhbGV0dGUpCgppZHguZmFtIDwtIGdyZXAoIl5MVFIiLCBnLmZhbS5uYW1lcywgdmFsdWUgPSBGQUxTRSkKdG1wLnBhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCcjQkZEQjM4JywgJyM1NEI0MzUnKSkobGVuZ3RoKGlkeC5mYW0pKQppZHgucGFsbGV0ZSA9IGMoaWR4LnBhbGxldGUsIGlkeC5mYW0pCmZhbS5wYWxldHRlID0gYyhmYW0ucGFsZXR0ZSwgdG1wLnBhbGV0dGUpCgppZHguZmFtIDwtIGdyZXAoIl5ETkEiLCBnLmZhbS5uYW1lcywgdmFsdWUgPSBGQUxTRSkKdG1wLnBhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCcjRjlCNUQwJywgJyM5NzE1NDknKSkobGVuZ3RoKGlkeC5mYW0pKQppZHgucGFsbGV0ZSA9IGMoaWR4LnBhbGxldGUsIGlkeC5mYW0pCmZhbS5wYWxldHRlID0gYyhmYW0ucGFsZXR0ZSwgdG1wLnBhbGV0dGUpCgppZHguZmFtID0gc2V0ZGlmZigxOmxlbmd0aChnLmZhbS5uYW1lcyksIGlkeC5wYWxsZXRlKQp0bXAucGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoJyNGRkMyNkYnLCAnI0MzODE1NCcsICcjODg0QTM5JywgJyM0RTM2MzYnKSkobGVuZ3RoKGlkeC5mYW0pKQppZHgucGFsbGV0ZSA9IGMoaWR4LnBhbGxldGUsIGlkeC5mYW0pCmZhbS5wYWxldHRlID0gYyhmYW0ucGFsZXR0ZSwgdG1wLnBhbGV0dGUpCgpuYW1lcyhmYW0ucGFsZXR0ZSkgPSBnLmZhbS5uYW1lc1tpZHgucGFsbGV0ZV0KZmFtLnBhbGV0dGVbJ1VuYXNzaWduZWQnXSA9ICdncmV5JwpmYW0ucGFsZXR0ZVsnTWl4J10gPSAnYmxhY2snCmZhbS5wYWxldHRlWydURUcnXSA9ICdkYXJrZ3JlZW4nCgoKCgpgYGAKCmBgYHtyfQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgCnAgPSBwICsgZ3VpZGVzKHNpemUgPSBGKSAKcCA9IHAgKyBjb29yZF9maXhlZChyYXRpbyA9IDEpCgoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF90ZXNfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF90ZXNfZmFtaWx5X2xlZ2VuZC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNywgaGVpZ2h0ID0gNSkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKCgoKCiMjIFNlcGFyYXRlbHkgdmlzdWFsaXNlIGNvbm5lY3RlZCBjb21wb25lbnRzCmBgYHtyfQp0bXAuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoYi5ncmFwaCksIGRpcmVjdGVkID0gVCkKdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQp0bXAuY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModG1wLmdyYXBoKQoKdG1wLmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXApCnRtcC5jbnQgPSAtc29ydCgtdG1wLmNudCkKaGVhZCh0bXAuY250KQoKayA9IDEKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwID09IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuYmlnIDwtIG5ldHdvcmsoYi5ncmFwaC5zdWIsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzLnN1Yi5iaWcgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQuc3ViLmJpZykKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAogICAgICAgICAgICBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKcC5iaWcudHlwZSA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIHNldC5zZWVkKDIwKQojIHAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKIyAgICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKIyAgICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiMgICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiMgICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKIyBwLmJpZy5jb2xvciA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQp0bXAubmFtZXMgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKVt0bXAuY29tcCRtZW1iZXJzaGlwICE9IHRtcC5rXQpiLmdyYXBoLnN1YiA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAoYi5ncmFwaFssMl0gJWluJSB0bXAubmFtZXMpLF0KCmcucGFydC5zdWIuc21hbGwgPC0gbmV0d29yayhiLmdyYXBoLnN1YiwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMuc3ViLnNtYWxsID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1Yi5zbWFsbCkKCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5zbWFsbCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQpwLnNtYWxsLnR5cGUgPXAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIHNldC5zZWVkKDIwKQojIHAgPC0gZ2duZXQyKGcucGFydC5zdWIuc21hbGwsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAojICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwgCiMgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiMgICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKIyAgICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKQojIHAuc21hbGwuY29sb3IgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCgpgYGAKIyMjIFBsb3RzCmBgYHtyfQpwLmJpZy50eXBlCnAuc21hbGwudHlwZQoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF90ZXNfZmFtaWx5X3NtYWxsLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpwcmludChwLnNtYWxsLnR5cGUpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3Rlc19mYW1pbHlfYmlnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwLmJpZy50eXBlKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKIyBTdG9wIGZvciB0aGUgcGFwZXIKYGBge3J9CnN0b3AoKQpgYGAKCgojIyBTcGVjaWZpYyBURSBmYW1pbGllcwojIyMgR3JhcGggb2Ygb25lIGZhbWlseQoKYGBge3J9CnNvcnQoLXRhYmxlKGRmLnF1ZXJ5JHN1YmZhbVsoZGYucXVlcnkkdmFsLmhpdHMgPT0gMykgJiAoZGYucXVlcnkkZmFtaWx5ID09ICdMVFIvQ29waWEnKV0pKQpgYGAKCgoKYGBge3J9CgojIG9uZS50ZS5mYW0gPSAnQlJPRFlBR0ExJwojIG9uZS50ZS5mYW0gPSAnQlJPRFlBR0EyJwojIG9uZS50ZS5mYW0gPSAnSEVMSVRST05ZMUQnCiMgb25lLnRlLmZhbSA9ICdIRUxJVFJPTlkzJwpvbmUudGUuZmFtID0gJ0FUQ09QSUE0MScKcXVlcnkuZmFtID0gZGYucXVlcnkkcXVlcnlbZGYucXVlcnkkc3ViZmFtID09IG9uZS50ZS5mYW1dCgoKb25lLnRlLmZhbSA9ICdBVENPUElBNDEnCnF1ZXJ5LmZhbSA9IGRmLnF1ZXJ5JHF1ZXJ5W2RmLnF1ZXJ5JHN1YmZhbSA9PSBvbmUudGUuZmFtXQoKcmVzLm5lc3QuZmFtcCA9IHJlcy5uZXN0WyhyZXMubmVzdCRWMSAlaW4lIHF1ZXJ5LmZhbSkgfCAocmVzLm5lc3QkVjggJWluJSBxdWVyeS5mYW0pLF0KCgppZHggPSByZXMubmVzdC5mYW1wJHAxID49IHNpbS5jdXRvZmYKZWRnZXMgPSBjYmluZChyZXMubmVzdC5mYW1wJFYxW2lkeF0sIHJlcy5uZXN0LmZhbXAkVjhbaWR4XSkKaWR4ID0gcmVzLm5lc3QuZmFtcCRwOCA+PSBzaW0uY3V0b2ZmCmVkZ2VzID0gcmJpbmQoZWRnZXMsIGNiaW5kKHJlcy5uZXN0LmZhbXAkVjhbaWR4XSwgcmVzLm5lc3QuZmFtcCRWMVtpZHhdKSkKCgp0ZS5lbmdlcy5uYW1lcyA9IHVuaXF1ZShjKGVkZ2VzWywxXSwgZWRnZXNbLDJdKSkKdGUuZW5nZXMuZmFtID0gc2FwcGx5KHRlLmVuZ2VzLm5hbWVzLCBmdW5jdGlvbihzKSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs5XSApCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdETkEvUG9nbycsICdETkEvVGMxJywgJ0ROQS9IYXJiaW5nZXInLCAnRE5BL0VuLVNwbScsCiAgICAgICAgICAgICAgICAgICAgICdETkEvSEFUJywgJ0ROQScsICdETkEvTWFyaW5lcicpXSA9ICdETkEnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdSYXRoRTFfY29ucycsICdSYXRoRTJfY29ucycsICdSYXRoRTNfY29ucycpXSA9ICdSYXRoRTEvMi8zX2NvbnMnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdMSU5FL0wxJywgJ0xJTkU/JyldID0gJ0xJTkUnCnRlLmVuZ2VzLmZhbVt0ZS5lbmdlcy5mYW0gJWluJSBjKCdVbmFzc2lnbmVkJyldID0gJ01peCcKdGUuZW5nZXMuZmFtW3RlLmVuZ2VzLmZhbSAlaW4lIGMoJ1JDL0hlbGl0cm9uJyldID0gJ0hlbGl0cm9uJwoKZy5wYXJ0IDwtIG5ldHdvcmsoZWRnZXMsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQpiLmdyYXBoLmxlbiA9IGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KGIuZ3JhcGgubmFtZXMsICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs1XSkpCgoKbGFiZWwuZmFtaWx5ID0gc2FwcGx5KHN0cnNwbGl0KGIuZ3JhcGgubmFtZXMsICJcXHwiKSwgZnVuY3Rpb24oeCkgeFs4XSkKbGFiLmNvbHMgPSBjKCcjM0YyRTNFJywgIndoaXRlIikKbGFiZWwuY29sb3IgPSBsYWIuY29sc1sobGFiZWwuZmFtaWx5ID09IG9uZS50ZS5mYW0pICsgMV0KCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gYi5ncmFwaC5sZW4sIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDE1LAogICAgICAgICAgICBhbHBoYT0wLjgsCiAgICAgICAgICAgIGFycm93LmdhcCA9IDAuMDE1LAogICAgICAgICAgICBhcnJvdy5zaXplID0gNSwKICAgICAgICAgICAgbGFiZWwuY29sb3IgPSBsYWJlbC5jb2xvciwKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGNvbG9yID0gdGUuZW5nZXMuZmFtW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUsCiAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikKcCAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdyZWFsX3Rlc19zdWJmYW1fJywgb25lLnRlLmZhbSwgJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gMjAsIGhlaWdodCA9IDE4KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gYi5ncmFwaC5uYW1lcywgZWRnZS5jb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICBub2RlLnNpemUgPSAxNSwKICAgICAgICAgICAgYWxwaGE9MC44LAogICAgICAgICAgICBhcnJvdy5nYXAgPSAwLjAxNSwKICAgICAgICAgICAgYXJyb3cuc2l6ZSA9IDUsCiAgICAgICAgICAgICMgbGFiZWwuY29sb3IgPSBsYWJlbC5jb2xvciwKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgY29sb3IgPSB0ZS5lbmdlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSwKICAgICAgICAgICAgIyBtb2RlID0gImthbWFkYWthd2FpIgogICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3JlYWxfdGVzX3N1YmZhbV8nLCBvbmUudGUuZmFtLCAnX25hbWVzLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1MCwgaGVpZ2h0ID0gNDkpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKIyMgRG90cGxvdHMKIyMjIyBGdW5jdGlvbnMKYGBge3J9CgoKc2VxMm14IDwtIGZ1bmN0aW9uKHNlcSwgd3NpemUpewogIAogIG51bV9yb3dzIDwtIGxlbmd0aChzZXEpIC0gd3NpemUgKyAxCiAgbWF0cml4X3NlcSA8LSBtYXRyaXgobnJvdyA9IG51bV9yb3dzLCBuY29sID0gd3NpemUpCiAgZm9yIChpIGluIDE6bnVtX3Jvd3MpIHsKICAgIG1hdHJpeF9zZXFbaSwgXSA8LSBzZXFbaTooaSArIHdzaXplIC0gMSldCiAgfQoKICByZXR1cm4obWF0cml4X3NlcSkKfQoKbXhDb21wIDwtIGZ1bmN0aW9uKG14MSwgbXgyLCB3c2l6ZSwgbm1hdGNoKXsKICBteC5yZXMgPSAwCiAgZm9yKHMgaW4gYygnQScsICdDJywgJ0cnLCAnVCcpKXsKICAgIG14LnJlcyA9IG14LnJlcyArIChteDEgPT0gcykgJSolIHQobXgyID09IHMpCiAgfQogICMgbXgucmVzID0gKG14LnJlcyA+PSBubWF0Y2gpICogMQogIG14LnJlc1tteC5yZXMgPCBubWF0Y2hdID0gMAogIAogIGluZGljZXMgPC0gd2hpY2gobXgucmVzICE9IDAsIGFyci5pbmQgPSBUUlVFKQogIHZhbHVlcyA8LSBteC5yZXNbaW5kaWNlc10KICByZXN1bHQgPC0gY2JpbmQoaW5kaWNlcywgdmFsdWVzKQogIHJlc3VsdCA9IGFzLmRhdGEuZnJhbWUocmVzdWx0KQogIHJldHVybihyZXN1bHQpCn0KCmRvdHBsb3QgPC0gZnVuY3Rpb24oc2VxMSwgc2VxMiwgd3NpemUsIG5tYXRjaCkgewogIHNlcTIucmMgPSByZXYoc2VxaW5yOjpjb21wKHNlcTIpKQoKICBteDEgPSB0b3VwcGVyKHNlcTJteChzZXExLCB3c2l6ZSkpCiAgbXgyID0gdG91cHBlcihzZXEybXgoc2VxMiwgd3NpemUpKQogIAogIHJlc3VsdCA9IG14Q29tcChteDEsIG14Miwgd3NpemUsIG5tYXRjaCkKICAKICBteDIucmMgPSB0b3VwcGVyKHNlcTJteChzZXEyLnJjLCB3c2l6ZSkpCiAgcmVzdWx0LnJjID0gbXhDb21wKG14MSwgbXgyLnJjLCB3c2l6ZSwgbm1hdGNoKQogIHJlc3VsdC5yYyR2YWx1ZXMgPSAtcmVzdWx0LnJjJHZhbHVlcwogIHJlc3VsdC5yYyRjb2wgPSBsZW5ndGgoc2VxMikgLSByZXN1bHQucmMkY29sIC0gd3NpemUgKyAyCiAgcmVzdWx0ID0gcmJpbmQocmVzdWx0LnJjLCByZXN1bHQpCiAgCiAgCiAgcCA9IGdncGxvdChyZXN1bHQsIGFlcyh4ID0gcm93LCB5ID0gY29sLCBmaWxsID0gdmFsdWVzKSkgKwogICAgZ2VvbV90aWxlKHdpZHRoID0gMSwgaGVpZ2h0ID0gMSkgKwogICAgIyB4bGFiKG5hbWUxKSArIHlsYWIobmFtZTIpICsKICAgICMgeGxhYihwYXN0ZTAoc3Ryc3BsaXQobmFtZTEsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKSkgKyAKICAgICMgeWxhYihwYXN0ZTAoc3Ryc3BsaXQobmFtZTIsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKSkgKwogICAgIyB4bGFiKCcnKSArIHlsYWIoJycpICsKICAgIGd1aWRlcyhmaWxsID0gRkFMU0UpICsKICAgIHRoZW1lX21pbmltYWwoKSArIGNvb3JkX2ZpeGVkKCkgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsgCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gIiNDRTFGNkEiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gIiMyNzM3NEQiLCBtaWRwb2ludCA9IDApICsKICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleSIsIGZpbGw9TkEsIHNpemU9MSkpCiAgcCAKICByZXR1cm4ocCkKfQoKYGBgCgpgYGB7cn0KZmlsZS50ZS5mYXN0YSA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3RhaXIvbmV3X2ZpbHRyYXRpb24vbmV3X3RlLmZhc3RhJwp0ZS5mYXN0YSA9IHNlcWlucjo6cmVhZC5mYXN0YShmaWxlLnRlLmZhc3RhKQp0ZS5uYW1lcyA9IG5hbWVzKHRlLmZhc3RhKQp0ZS5mYXN0YSA9IHNlcWlucjo6Z2V0U2VxdWVuY2UodGUuZmFzdGEpCm5hbWVzKHRlLmZhc3RhKSA9IHRlLm5hbWVzCmBgYAoKIyMjIE9uZSBwYWlyd2lzZSBleGFtcGxlCmBgYHtyfQp3c2l6ZSA9IDEwCm5tYXRjaCA9IDgKCnNlcTEgPSB0ZS5mYXN0YVtbYi5ncmFwaC5uYW1lc1sxXV1dCnNlcTIgPSB0ZS5mYXN0YVtbYi5ncmFwaC5uYW1lc1sxXV1dCgoKbmFtZTEgPSAndGV8MTIzODQ3NjN8MTIzODUyNjJ8NHw1MDB8K3xBVDRURTU3NTgwfEJST0RZQUdBMUF8RE5BL011RFInCm5hbWUyID0gJ3RlfDEzNjc0OTE3fDEzNjc1MjcxfDF8MzU1fCt8QVQxVEU0NDc2MHxCUk9EWUFHQTF8RE5BL011RFInCgojIG5hbWUxID0gJ3RlfDEwNTkyMTExfDEwNTkyNjY0fDF8NTU0fC18QVQxVEUzNDI2NXxCUk9EWUFHQTJ8RE5BL011RFInCiMgbmFtZTIgPSAndGV8ODc0MzIzOHw4NzQ0MjYzfDR8MTAyNnwtfEFUNFRFMzkwNDV8SEVMSVRST05ZMUR8UkMvSGVsaXRyb24nCgpuYW1lMSA9ICd0ZXw2MjgzMTk4fDYyODQ0MjF8NHwxMjI0fC18QVQ0VEUyNjcxMHxBVFJFUDE1fFJDL0hlbGl0cm9uJwpuYW1lMiA9ICd0ZXw2MjgzMTk4fDYyODQ0MjF8NHwxMjI0fC18QVQ0VEUyNjcxMHxBVFJFUDE1fFJDL0hlbGl0cm9uJwoKc2VxMSA9IHRlLmZhc3RhW1tuYW1lMV1dCnNlcTIgPSB0ZS5mYXN0YVtbbmFtZTJdXQoKcCA9IGRvdHBsb3Qoc2VxMSwgc2VxMiwgd3NpemUsIG5tYXRjaCkKCnAgPSBwICsgYW5ub3RhdGUoInRleHQiLCB4ID0gLUluZiwgeSA9IEluZiwgbGFiZWwgPSBwYXN0ZSgnd3NpemU9Jyx3c2l6ZSwnXG5ubWF0Y2g9JyxubWF0Y2gsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICBoanVzdCA9IC0wLjEsIHZqdXN0ID0gMS4xKQoKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlXycsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKYGBgCgojIyMgb25lIFZTIGFsbApgYGB7cn0KCndzaXplID0gMTAKbm1hdGNoID0gOAoKbmFtZTAgPSAndGV8MTE2ODM1NjV8MTE2ODk4MjF8M3w2MjU3fCt8QVQzVEU0ODU0MHxBVENPUElBOTV8TFRSL0NvcGlhJwpuYW1lMCA9ICd0ZXwxNjY5MTc0OHwxNjY5NTE1NHwxfDM0MDd84oiSfEFUMVRFNTUwNzB8QVRDT1BJQTQxfExUUi9Db3BpYScKbmFtZTAgPSBnc3ViKCfiiJInLCAiLSIsIG5hbWUwKQoKCm9uZS50ZS5mYW0gPSBzdHJzcGxpdChuYW1lMCwgJ1xcfCcpW1sxXV1bOF0KIyBvbmUudGUuZmFtID0gJ0JST0RZQUdBMicKcXVlcnkuZmFtID0gZGYucXVlcnkkcXVlcnlbZGYucXVlcnkkc3ViZmFtID09IG9uZS50ZS5mYW1dCnF1ZXJ5LmZhbSA9IHF1ZXJ5LmZhbVsocXVlcnkuZmFtICVpbiUgcmVzLm5lc3Quc2ltJFYxKSB8IChxdWVyeS5mYW0gJWluJSByZXMubmVzdC5zaW0kVjIpXQoKbmFtZXMuYWxsID0gc2V0ZGlmZihxdWVyeS5mYW0sIG5hbWUwKQoKcC5hbGwgPSBsaXN0KCkKZm9yKG5hbWUyIGluIG5hbWVzLmFsbCl7CiAgIyBtZXNzYWdlKG5hbWUyKQogIHNlcTEgPSB0ZS5mYXN0YVtbbmFtZTBdXQogIHNlcTIgPSB0ZS5mYXN0YVtbbmFtZTJdXQogIAogIHMxID0gc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzddCiAgczIgPSBzdHJzcGxpdChuYW1lMiwgJ1xcfCcpW1sxXV1bN10KICBwID0gZG90cGxvdChzZXExLCBzZXEyLCB3c2l6ZSwgbm1hdGNoKSArIHhsYWIoczEpICsgeWxhYihzMikKICBwLmFsbFtbbmFtZTJdXSA9IHAKfQojIAojIHBwID0gZ3JpZC5hcnJhbmdlKGdyb2JzID0gcC5hbGwsIG5jb2wgPSAxMykgIyMgZGlzcGxheSBwbG90CiMgCiMgCiMgcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ3BhaXJ3aXNlX2FsbCcsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNTAsIGhlaWdodCA9IDUwKQojIHByaW50KHBwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgojIGRldi5vZmYoKQoKczAgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ18nKQpzMCA9IGdzdWIoIi8iLCAiLSIsIHMwKQpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAncGFpcndpc2VfYWxsXycsczAsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNTAsIGhlaWdodCA9IDUwKQpncmlkLmFycmFuZ2UoZ3JvYnMgPSBwLmFsbCwgbmNvbCA9IGNlaWxpbmcoc3FydChsZW5ndGgocC5hbGwpKSkpICMgV3JpdGUgdGhlIGdyaWQuYXJyYW5nZSBpbiB0aGUgZmlsZQpkZXYub2ZmKCkgIyBDbG9zZSB0aGUgZmlsZQoKYGBgCgoKIyMjIG9uZSBjb25uZWN0ZWQgY29tcG9uZW50CmBgYHtyfQoKd3NpemUgPSAxMApubWF0Y2ggPSA4CgoKbmFtZTAgPSAndGV8NjIwNTYyMXw2MjA2MTg0fDJ8NTY0fOKIknxBVDJURTI1MjU1fEhFTElUUk9OWTFEfFJDL0hlbGl0cm9uJwpuYW1lMCA9ICd0ZXwxNDE4OTI1NnwxNDE5MDI2Nnw1fDEwMTF8LXxBVDVURTUwNzAwfEhFTElUUk9OWTN8UkMvSGVsaXRyb24nCm5hbWUwID0gJ3RlfDEyNTEzMjM5fDEyNTEzODI0fDF8NTg2fCt8QVQxVEU0MDcyNXxBVEhJTEE0QXxMVFIvR3lwc3knCm5hbWUwID0gJ3RlfDExNjQ3NDI2fDExNjQ4OTEyfDF8MTQ4N3wrfEFUMVRFMzc3MDV8QVRSRVA3fFJDL0hlbGl0cm9uJwpuYW1lMCA9ICd0ZXwxMTY4MzU2NXwxMTY4OTgyMXwzfDYyNTd8K3xBVDNURTQ4NTQwfEFUQ09QSUE5NXxMVFIvQ29waWEnCm5hbWUwID0gZ3N1Yign4oiSJywgIi0iLCBuYW1lMCkKCgpuYW1lcy5hbGwgPSB1bmlxdWUoYyhyZXMubmVzdC5zaW0kVjFbcmVzLm5lc3Quc2ltJFY4ID09IG5hbWUwXSwKICAgICAgICAgICAgICAgICAgICAgcmVzLm5lc3Quc2ltJFY4W3Jlcy5uZXN0LnNpbSRWMSA9PSBuYW1lMF0pKQojIG5hbWVzLmFsbCA9IHVuaXF1ZShjKHJlcy5uZXN0JFYxW3Jlcy5uZXN0JFY4ID09IG5hbWUwXSwgCiMgICAgICAgICAgICAgICAgICAgICAgcmVzLm5lc3QkVjhbcmVzLm5lc3QkVjEgPT0gbmFtZTBdKSkKCnAuYWxsID0gbGlzdCgpCmZvcihuYW1lMiBpbiBuYW1lcy5hbGwpewogICMgbWVzc2FnZShuYW1lMikKICBzZXExID0gdGUuZmFzdGFbW25hbWUwXV0KICBzZXEyID0gdGUuZmFzdGFbW25hbWUyXV0KICAKICBzMSA9IHBhc3RlMChzdHJzcGxpdChuYW1lMCwgJ1xcfCcpW1sxXV1bNzo5XSwgY29sbGFwc2UgPSAnfCcpCiAgczIgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTIsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ3wnKQogIHAgPSBkb3RwbG90KHNlcTEsIHNlcTIsIHdzaXplLCBubWF0Y2gpICsgeGxhYihzMSkgKyB5bGFiKHMyKQogIHAuYWxsW1tuYW1lMl1dID0gcAp9CgoKczAgPSBwYXN0ZTAoc3Ryc3BsaXQobmFtZTAsICdcXHwnKVtbMV1dWzc6OV0sIGNvbGxhcHNlID0gJ18nKQpzMCA9IGdzdWIoIi8iLCAiLSIsIHMwKQpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAncGFpcndpc2VfY29ubmVjdF8nLHMwLCcucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUwLCBoZWlnaHQgPSA1MCkKZ3JpZC5hcnJhbmdlKGdyb2JzID0gcC5hbGwsIG5jb2wgPSBjZWlsaW5nKHNxcnQobGVuZ3RoKHAuYWxsKSkpKSAjIFdyaXRlIHRoZSBncmlkLmFycmFuZ2UgaW4gdGhlIGZpbGUKZGV2Lm9mZigpICMgQ2xvc2UgdGhlIGZpbGUKCmBgYAoKCgpgYGB7cn0KCgpuYW1lMSA9ICd0ZXwxNDE4OTI1NnwxNDE5MDI2Nnw1fDEwMTF8LXxBVDVURTUwNzAwfEhFTElUUk9OWTN8UkMvSGVsaXRyb24nCm5hbWUyID0gJ3RlfDIxNjIyOTV8MjE2MjkzN3wyfDY0M3wtfEFUMlRFMDk5NTB8SEVMSVRST05ZM3xSQy9IZWxpdHJvbicKbmFtZTAgPSBuYW1lMQoKbmFtZXMuYWxsID0gdW5pcXVlKGMocmVzLm5lc3QkVjFbcmVzLm5lc3QkVjggPT0gbmFtZTBdLCByZXMubmVzdCRWOFtyZXMubmVzdCRWMSA9PSBuYW1lMF0pKQoKCm5hbWVzID0gYyhuYW1lMSwgbmFtZTIpCmIudG1wID0gYmwucmVzWyhibC5yZXMkVjEgJWluJSBuYW1lcykgJiAoYmwucmVzJFY4ICVpbiUgbmFtZXMpLF0KCnJlcy5uZXN0WyhyZXMubmVzdCRWMSAlaW4lIG5hbWVzKSAmIChyZXMubmVzdCRWOCAlaW4lIG5hbWVzKSwgXQoKYGBgCgoKIyBTVnMKIyMgUmVhZGluZ3Mgc2VTVnMKYGBge3J9Cgpzdi5zZSA9IHJlYWRSRFMocGFzdGUocGF0aC5zdnMsICdzdl9zZS5yZHMnLCBzZXAgPSAnJykpCgojIFJlbmFtZSBsZW5ndGggZ3JvdXBzCmxldi5yZXBsYWNlID0gYygnWzEsMTBdJywgJygxMCwxNV0nKQpsZXYubmV3ID0gJ1sxLDE1XScKCnMubGV2ZWxzID0gYXMuY2hhcmFjdGVyKGxldmVscyhzdi5zZSRsZW4uZ3IpKQpzLmxldmVscyA9IHMubGV2ZWxzWyEocy5sZXZlbHMgJWluJSBsZXYucmVwbGFjZSldCnMubGV2ZWxzID0gYyhsZXYubmV3LCBzLmxldmVscykKcy5sZXZlbHMgPSBnc3ViKCJlXFwrMDMiLCAiayIsIHMubGV2ZWxzKQoKc3Yuc2UkbGVuLmdyID0gYXMuY2hhcmFjdGVyKHN2LnNlJGxlbi5ncikKc3Yuc2UkbGVuLmdyW3N2LnNlJGxlbi5nciAlaW4lIGxldi5yZXBsYWNlXSA9IGxldi5uZXcKc3Yuc2UkbGVuLmdyID0gZ3N1YigiZVxcKzAzIiwgImsiLCBzdi5zZSRsZW4uZ3IpCnN2LnNlJGxlbi5nciA9IGZhY3Rvcihzdi5zZSRsZW4uZ3IsIGxldmVscyA9IHMubGV2ZWxzKQoKCiMgUmVwbGFjZSBmYW1pbGllcwpzdi5zZSRmYW0gPSBhcy5jaGFyYWN0ZXIoc3Yuc2UkZmFtKQpzdi5zZSRmYW0gPC0gZ3N1YigiSGVsaXRyb24vLioiLCAiTWl4IHdpdGggSGVsaXRyb24iLCBzdi5zZSRmYW0pCgoKc3Yuc2UkdGUgPSBmYWN0b3Ioc3Yuc2UkdGUsIGxldmVscyA9IGMoJ2lzVEUnLCAnaXNURXBhcnQnLCAnaGFzVEUnLCAnaGFzVEVwYXJ0JywgJ25vVEUnKSkKCgoKYGBgCgojIyBSZWFkaW5nIG5lc3RlZG5lc3MKYGBge3J9CgojIExvYWQgc2ltaWxhcml0eSBmdW5jdGlvbgoKZmlsZS5uZXN0ZWRuZXNzID0gcGFzdGUocGF0aC53b3JrLCAnc3ZfYmlnX29uX2JpZ19uZXN0LnJkcycsIHNlcCA9ICcnKQoKCmlmKCFmaWxlLmV4aXN0cyhmaWxlLm5lc3RlZG5lc3MpKXsKICBibC5maWxlID0gcGFzdGUocGF0aC53b3JrLCAnc3ZfYmlnX29uX2JpZy50eHQnLCBzZXAgPSAnJykKICBibC5yZXMgPSByZWFkLnRhYmxlKGJsLmZpbGUpCiAgYmwucmVzID0gYmwucmVzW2JsLnJlcyRWMSAhPSBibC5yZXMkVjgsXQoKICByZXMubmVzdCA9IGZpbmROZXN0ZWRuZXNzKGJsLnJlcywgdXNlLnN0cmFuZCA9IEYpCiAgICAKICByZXMubmVzdCRsZW4xID0gcmVzLm5lc3QubGVuW3Jlcy5uZXN0JFYxXQogIHJlcy5uZXN0JGxlbjggPSByZXMubmVzdC5sZW5bcmVzLm5lc3QkVjhdCiAgcmVzLm5lc3QkcDEgPSByZXMubmVzdCRDMSAvIHJlcy5uZXN0JGxlbjEKICByZXMubmVzdCRwOCA9IHJlcy5uZXN0JEM4IC8gcmVzLm5lc3QkbGVuOCAgCiAgc2F2ZVJEUyhyZXMubmVzdCwgZmlsZS5uZXN0ZWRuZXNzLCBjb21wcmVzcyA9IEYpCn0gZWxzZSB7CiAgcmVzLm5lc3QgPSByZWFkUkRTKGZpbGUubmVzdGVkbmVzcykKfQoKcmVzLm5lc3QubGVuID0gc2FwcGx5KHVuaXF1ZShjKHJlcy5uZXN0JFYxLCByZXMubmVzdCRWOCkpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bMl0pKQpyZXMubmVzdDAgPSByZXMubmVzdAoKCgpgYGAKCgojIyBURSBzdGF0CiMjIyBJbiBncmFwaCAtIG5vdCBpbiBncmFwaApgYGB7cn0KcmVzLm5lc3QgPSByZXMubmVzdDAKCnN2LnNlLmxlbiA9IHN2LnNlW3N2LnNlJGxlbiA+PSAxMDAsXQpzdi5zZS5sZW4kaW4uY29ubmVjdCA9IHN2LnNlLmxlbiRuYW1lICVpbiUgbmFtZXMocmVzLm5lc3QubGVuKQoKY250LnN2LnNlID0gdGFibGUoc3Yuc2UubGVuJGluLmNvbm5lY3QgLCBzdi5zZS5sZW4kdGUpCmNudC5zdi5zZQoKZGYgPSByZXNoYXBlMjo6bWVsdChjbnQuc3Yuc2UpCgp0ZS5jb250ZW50Lm5hbWVzID0gYygibm9URSIsICJpc1RFIiwgImhhc1RFIiwgImhhc1RFcGFydCIsICJpc1RFcGFydCIpCmNvbHMgPSBjKCcjRDhEOUNGJywgJyNFQjQ1NUYnLCAnIzdCNjA3OScsICcjM0M4REFEJywgJyM3OUI3NzMnKQpuYW1lcyhjb2xzKSA9IHRlLmNvbnRlbnQubmFtZXMKCmRmJFZhcjIgPSBmYWN0b3IoZGYkVmFyMiwgbGV2ZWxzID0gcmV2KGMoJ2lzVEUnLCAnaXNURXBhcnQnLCAnaGFzVEUnLCAnaGFzVEVwYXJ0JywgJ25vVEUnKSkpCgoKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3BhdHRlcm4iKQoKCnAgPSBnZ3Bsb3QoZGYsIGFlcyh4ID0gVmFyMiwgeSA9IHZhbHVlLCBmaWxsID0gVmFyMiwgYWxwaGEgPSBWYXIxLCBjb2xvciA9IFZhcjEpKSArCiAgZ2VvbV9jb2xfcGF0dGVybiggYWVzKHBhdHRlcm4gPSBWYXIxKSwKICAgICMgcGF0dGVybiA9IHJlcChjKCdub25lJywgInN0cmlwZSIpLCA1KSwKICAgIHBhdHRlcm5fZGVuc2l0eSA9IDAuMSwKICAgIHBhdHRlcm5fc3BhY2luZyA9IDAuMDI1LAogICAgcGF0dGVybl9maWxsID0gImdyZXk3MCIsIAogICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgIHdpZHRoID0gMC44CiAgKSArIAogICMgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiLCB3aWR0aCA9IDAuOCkgKwogIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKDAuOCwgMSksIGxhYmVscyA9IGMoIk5vIiwgIlllcyIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2JsYWNrJywgJ2JsYWNrJyksIGxhYmVscyA9IGMoIm5vdCBpbiBncmFwaCIsICJpbiBncmFwaCIpKSArCiAgc2NhbGVfcGF0dGVybl9tYW51YWwodmFsdWVzID0gYygic3RyaXBlIiwgJ25vbmUnKSwgbGFiZWxzID0gYygiaW4gZ3JhcGgiLCAibm90IGluIGdyYXBoIiksCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhUUlVFLCBGQUxTRSkpICsKICBsYWJzKGZpbGwgPSAiIiwgcGF0dGVybj0nQ29ubmVjdGVkIHRvIG90aGVycycpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xzKSArCiAgeGxhYihOVUxMKSArCiAgeWxhYigiTnVtYmVyIG9mIFNWcyIpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBndWlkZXMoYWxwaGEgPSAibm9uZSIsIGZpbGwgPSAnbm9uZScsIGNvbG9yID0gJ25vbmUnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgY29vcmRfZmxpcCgpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC43LCAwLjMpLCAgICAgIyBBZGp1c3QgdGhlc2UgY29vcmRpbmF0ZXMgYXMgbmVlZGVkCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJ0cmFuc3BhcmVudCIsIGNvbG9yPSdncmV5NzAnKSAgCiAgKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBndWlkZXMocGF0dGVybiA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSBjKCJ3aGl0ZSIpLCBjb2xvcj0gJ2JsYWNrJykpKSAgCnAKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfaW5fZ3JhcGgucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDMsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKCiMjIyBURSBmYW1pbGllcyBpbiBTViB0eXBlcwpgYGB7cn0KCnN2LnNlLmxlbiA9IHN2LnNlW3N2LnNlJGxlbiA+PSAxMDAsXQpjbnQuZmFtLnN2ID0gdGFibGUoc3Yuc2UubGVuJGZhbVtzdi5zZS5sZW4kZmFtIT0nJ10sIHN2LnNlLmxlbiR0ZVtzdi5zZS5sZW4kZmFtIT0nJ10pCmNudC5mYW0uc3YgPSB0KGFwcGx5KGNudC5mYW0uc3YsIDEsIGZ1bmN0aW9uKHgpIHgvc3VtKHgpKSkKY250LmZhbS5zdiA9IGNudC5mYW0uc3ZbLCBjb2xTdW1zKGNudC5mYW0uc3YpICE9IDBdCmNudC5mYW0uc3YgPSByZXNoYXBlMjo6bWVsdChjbnQuZmFtLnN2KQoKcCA9IGdncGxvdChjbnQuZmFtLnN2LCBhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBjb2xvciA9IFZhcjIpKSArIAogIGdlb21fcG9pbnQoYWVzKHNpemUgPSB2YWx1ZSwgYWxwaGEgPSB2YWx1ZSAqIDIpKSArIHRoZW1lX21pbmltYWwoKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xzKSAgKwogIGdlb21fdGV4dChkYXRhID0gY250LmZhbS5zdltjbnQuZmFtLnN2JHZhbHVlID49IDAuMixdLCAKICAgICAgICAgICAgICBhZXMoeD1WYXIyLCB5PVZhcjEsIGxhYmVsID0gcm91bmQodmFsdWUsIDIpKSwgCiAgICAgICAgICAgICAgc2l6ZSA9IDIuNSwgY29sb3IgPSAnYmxhY2snLCAKICAgICAgICAgICAgbnVkZ2VfeCA9IDAuMywKICAgICAgICAgICAgbnVkZ2VfeSA9IDApICsKICBndWlkZXMoc2l6ZSA9ICJub25lIiwgYWxwaGEgPSAibm9uZSIsIGNvbG9yID0gJ25vbmUnKSArCiAgeGxhYignU1YgdHlwZScpICsgeWxhYignVEUgZmFtaWx5JykKcAoKCmNudC5mYW0uc3YgPSByb3dTdW1zKHRhYmxlKHN2LnNlLmxlbiRmYW1bc3Yuc2UubGVuJGZhbSE9JyddLCBzdi5zZS5sZW4kdGVbc3Yuc2UubGVuJGZhbSE9JyddKSkKY250LmZhbS5zdiA9IGRhdGEuZnJhbWUodmFsdWUgPSBjbnQuZmFtLnN2LCBuYW1lcyA9IG5hbWVzKGNudC5mYW0uc3YpKQpyb3duYW1lcyhjbnQuZmFtLnN2KSA9IE5VTEwKCmcgPSBnZ3Bsb3QoY250LmZhbS5zdiwgYWVzKHggPSBuYW1lcywgeSA9IHZhbHVlKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0iZ3JleTgwIikrCiAgY29vcmRfZmxpcCgpICsgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBhc3RlKCIxZSIsc2VxKDAsNCwxKSwgc2VwID0gJycpLCBicmVha3M9IHNlcSgwLDQsMSkqMTAwMCkgKwogIHlsYWIoJyMnKSArIGdlb21fdGV4dChhZXMobGFiZWw9dmFsdWUsIHk9MCksIGhqdXN0PTAsIHNpemUgPSAyLjUpCmcgCgoKcHAgPSBnZ3B1YnI6OmdnYXJyYW5nZShwICsgeGxhYignVEUgY29udGVudCcpICsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCdpcyBjb21wbC4nLCAnaXMgZnJhZ20uJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY29udC4gY29tcGwuJywgJ2NvbnQuIGZyYWdtLicpKSAsIGcsIG5jb2wgPSAyLCB3aWR0aHMgPSBjKDAuNzUsIDAuMjUpKQpwcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl90ZV9mYW1fc3ZfdHlwZS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKcHJpbnQocHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCiMgSW5zZXJ0aW9uIGFuZCBkZWxldGlvbgppZHggPSAoc3Yuc2UubGVuJGZhbSE9JycpICYgKHN2LnNlLmxlbiRmcmVxLm1heCA8PSAzKQp0YWJsZShzdi5zZS5sZW4kZmFtW2lkeF0sIHN2LnNlLmxlbiR0ZVtpZHhdKQoKaWR4ID0gKHN2LnNlLmxlbiRmYW0hPScnKSAmIChzdi5zZS5sZW4kZnJlcS5tYXggPj0gMjUpICYgKHN2LnNlLmxlbiRsZW4gPj0gMTAwKSAKdGFibGUoc3Yuc2UubGVuJGZhbVtpZHhdLCBzdi5zZS5sZW4kdGVbaWR4XSkKYGBgCiMjIyBURSBmYW06IFRBSVIxMApgYGB7cn0KZi50ZS5yZWYgPSBwYXN0ZShwYXRoLnRhaXIsICduZXdfdGUuZmFzdGEnLCBzZXAgPSAnJykKbGluZXMgPSByZWFkTGluZXMoZi50ZS5yZWYpCmxpbmVzID0gZ3JlcCgnXj4nLCBsaW5lcywgdmFsdWUgPSBUKQoKcmVmLmZhbSA9IHNhcHBseShsaW5lcywgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgJ1xcfCcpW1sxXV1bOV0pCgoKaW5kaWNlcyA8LSBncmVwKCJeRE5BKD8hL0hBVHwvTXVEUikiLCByZWYuZmFtLCB2YWx1ZSA9IEZBTFNFLCBwZXJsID0gVFJVRSkKcmVmLmZhbVtpbmRpY2VzXSA9ICdETkErJwoKaW5kaWNlcyA8LSBncmVwKCJeUmF0aEUiLCByZWYuZmFtLCB2YWx1ZSA9IEZBTFNFLCBwZXJsID0gVFJVRSkKcmVmLmZhbVtpbmRpY2VzXSA9ICdSYXRoRTEvMi8zX2NvbnMnCgppbmRpY2VzIDwtIGdyZXAoIl5MSU5FIiwgcmVmLmZhbSwgdmFsdWUgPSBGQUxTRSwgcGVybCA9IFRSVUUpCnJlZi5mYW1baW5kaWNlc10gPSAnTElORScKcmVmLmZhbVtyZWYuZmFtID09ICdSQy9IZWxpdHJvbiddID0gJ0hlbGl0cm9uJwoKcmVmLmZhbS5jbnQgPSB0YWJsZShyZWYuZmFtKQoKCgpkZiA9IGNudC5mYW0uc3YKZGYkcmVmID0gYXMubnVtZXJpYyhyZWYuZmFtLmNudFtkZiRuYW1lc10pCmRmID0gZGZbIWlzLm5hKGRmJHJlZiksXQoKcGxvdChkZiR2YWx1ZSwgZGYkcmVmKQoKCgpwIDwtIGdncGxvdChkZiwgYWVzKHggPSByZWYsIHkgPSB2YWx1ZSwgY29sb3IgPSBuYW1lcykpICsKICBnZW9tX3Ntb290aChhZXMoZ3JvdXAgPSAxKSwgbWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiB4LCBzZSA9IEZBTFNFLCBjb2xvciA9ICdncmV5NzAnKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IG5hbWVzKSwgbWF4Lm92ZXJsYXBzID0gMjApICsKICAjIHhsYWIoImxvZyAjIGluIFRBSVIxMCBhbm5vdGF0aW9uIikgKwogICMgeWxhYigibG9nICMgaW4gU1ZzIikgKwogICMgc2NhbGVfeF9sb2cxMCgpICsKICAjIHNjYWxlX3lfbG9nMTAoKSArCiAgeGxhYigiIyBpbiBUQUlSMTAgYW5ub3RhdGlvbiIpICsKICB5bGFiKCIjIGluIFNWcyIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIHRoZW1lX21pbmltYWwoKQpwCgoKbG1fbW9kZWwgPC0gbG0odmFsdWUgfiByZWYsIGRhdGEgPSBkZikKc2xvcGUgPC0gY29lZihsbV9tb2RlbClbMl0KCgpwID0gcCArIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1pbihkZiRyZWYpLCB5ID0gbWF4KGRmJHZhbHVlKSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgnU2xvcGU6Jywgcm91bmQoc2xvcGUsIDMpKSwgaGp1c3QgPSAwLCB2anVzdCA9IDEpCgoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfdGVfZmFtX3RhaXIxMC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNCwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKYGBgCgoKCiMjIEZpbHRyYXRpb24KYGBge3J9CgpyZXMubmVzdCA9IHJlcy5uZXN0MAoKc3YubmFtZXMubWl4ID0gc3Yuc2UkbmFtZVtncmVwKCJeTWl4Iiwgc3Yuc2UkZmFtKV0KcmVzLm5lc3QgPSByZXMubmVzdFshKHJlcy5uZXN0JFYxICVpbiUgc3YubmFtZXMubWl4KSxdCnJlcy5uZXN0ID0gcmVzLm5lc3RbIShyZXMubmVzdCRWOCAlaW4lIHN2Lm5hbWVzLm1peCksXQoKCnN2Lm5hbWVzLm1peCA9IHN2LnNlJG5hbWVbc3Yuc2UkdGUgPT0gJ25vVEUnXQpyZXMubmVzdCA9IHJlcy5uZXN0WyEocmVzLm5lc3QkVjEgJWluJSBzdi5uYW1lcy5taXgpLF0KcmVzLm5lc3QgPSByZXMubmVzdFshKHJlcy5uZXN0JFY4ICVpbiUgc3YubmFtZXMubWl4KSxdCgpzaW5nbGV0b24ubW9kZSA9IEYKaWYoc2luZ2xldG9uLm1vZGUpewogIHN2Lm5hbWVzLmZyZXEgPSBzdi5zZSRuYW1lW3N2LnNlJGZyZXEubWF4IDw9IDNdCiAgIyBzdi5uYW1lcy5mcmVxID0gc3Yuc2UkbmFtZVtzdi5zZSRmcmVxLm1heCA+PSAyNV0KICByZXMubmVzdCA9IHJlcy5uZXN0W3Jlcy5uZXN0JFYxICVpbiUgc3YubmFtZXMuZnJlcSxdCiAgcmVzLm5lc3QgPSByZXMubmVzdFtyZXMubmVzdCRWOCAlaW4lIHN2Lm5hbWVzLmZyZXEsXQp9CgpwcmVmaXgubW9kZSA9IGMoJycsICdfc2luZ2xlJykKCgpgYGAKCgojIyBHcmFwaApgYGB7cn0KIyBhbGwgZWRnZXMKaWR4ID0gcmVzLm5lc3QkcDEgPj0gc2ltLmN1dG9mZgplZGdlcyA9IGNiaW5kKHJlcy5uZXN0JFYxW2lkeF0sIHJlcy5uZXN0JFY4W2lkeF0pCmlkeCA9IHJlcy5uZXN0JHA4ID49IHNpbS5jdXRvZmYKZWRnZXMgPSByYmluZChlZGdlcywgY2JpbmQocmVzLm5lc3QkVjhbaWR4XSwgcmVzLm5lc3QkVjFbaWR4XSkpCnRlLmVuZ2VzLm5hbWVzID0gdW5pcXVlKGMoZWRnZXNbLDFdLCBlZGdlc1ssMl0pKQoKdG1wID0gc3Yuc2UkdGUKbmFtZXModG1wKSA9IHN2LnNlJG5hbWUKdGUuZW5nZXMudHlwZSA9IGFzLmNoYXJhY3Rlcih0bXBbdGUuZW5nZXMubmFtZXNdKQpuYW1lcyh0ZS5lbmdlcy50eXBlKSA8LSBuYW1lcyh0bXBbdGUuZW5nZXMubmFtZXNdKQoKCnRtcCA9IHN2LnNlJGZhbQpuYW1lcyh0bXApID0gc3Yuc2UkbmFtZQp0ZS5lbmdlcy5mYW0gPSB0bXBbdGUuZW5nZXMubmFtZXNdCgojIG5vZGVzCmlkeCA9IChyZXMubmVzdCRwMSA+PSBzaW0uY3V0b2ZmKSAmIChyZXMubmVzdCRwOCA+PSBzaW0uY3V0b2ZmKQp0ZS5ub2RlcyA9IGNiaW5kKHJlcy5uZXN0JFYxW2lkeF0sIHJlcy5uZXN0JFY4W2lkeF0pCnRlLnJlc3QgPSBzZXRkaWZmKHRlLmVuZ2VzLm5hbWVzLCBjKHRlLm5vZGVzWywxXSwgdGUubm9kZXNbLDJdKSkKCgp0ZS5ub2Rlcy5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodCh0ZS5ub2RlcyksIGRpcmVjdGVkID0gVCkKdGUubm9kZXMuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeSh0ZS5ub2Rlcy5ncmFwaCkKdGUubm9kZXMuY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModGUubm9kZXMuZ3JhcGgpCgpub2RlcyA9IGRhdGEuZnJhbWUobm9kZSA9IHBhc3RlKCdOJywgdGUubm9kZXMuY29tcCRtZW1iZXJzaGlwLCBzZXAgPSAnJyksIHRlID0gbmFtZXModGUubm9kZXMuY29tcCRtZW1iZXJzaGlwKSkKCm5vZGVzLnJlc3QgPSBkYXRhLmZyYW1lKG5vZGUgPSBwYXN0ZSgnUicsICgxOmxlbmd0aCh0ZS5yZXN0KSksIHNlcCA9ICcnKSwgdGUgPSB0ZS5yZXN0KQpub2RlcyA9IHJiaW5kKG5vZGVzLCBub2Rlcy5yZXN0KQoKcm93bmFtZXMobm9kZXMpID0gbm9kZXMkdGUKCiMgRGVmaW5lIFRFIHR5cGUKbm9kZXMuY250ID0gZGF0YS5mcmFtZShjbnQgPSBjKHRhYmxlKG5vZGVzJG5vZGUpKSkKbm9kZXMuY250JG5vZGUgPSByb3duYW1lcyhub2Rlcy5jbnQpCm5vZGVzLmNudCR0eXBlID0gc2FwcGx5KG5vZGVzLmNudCRub2RlLCBmdW5jdGlvbihzKXsKICBzLnRlID0gbm9kZXMkdGVbbm9kZXMkbm9kZSA9PSBzXQogIHR5cGUudGUgPSB1bmlxdWUodGUuZW5nZXMudHlwZVtzLnRlXSkKICBpZihsZW5ndGgodHlwZS50ZSkgPT0gMSl7CiAgICByZXR1cm4odHlwZS50ZSkKICB9IGVsc2UgewogICAgdHlwZS50ZSA9IHRhYmxlKHR5cGUudGUpCiAgICB0eXBlLnRlID0gbmFtZXModHlwZS50ZSlbdHlwZS50ZSA9PSBtYXgodHlwZS50ZSldCiAgICByZXR1cm4odHlwZS50ZVsxXSkKICB9Cn0pCnRhYmxlKG5vZGVzLmNudCR0eXBlKQoKIyBEZWZpbmUgVEUgZmFtaWx5Cm5vZGVzLmNudCRmYW0gPSBzYXBwbHkobm9kZXMuY250JG5vZGUsIGZ1bmN0aW9uKHMpewogIHMudGUgPSBub2RlcyR0ZVtub2RlcyRub2RlID09IHNdCiAgdHlwZS50ZSA9IHVuaXF1ZSh0ZS5lbmdlcy5mYW1bcy50ZV0pCiAgaWYobGVuZ3RoKHR5cGUudGUpID09IDEpewogICAgcmV0dXJuKHR5cGUudGUpCiAgfSBlbHNlIHsKICAgIHR5cGUudGUgPSB0YWJsZSh0eXBlLnRlKQogICAgdHlwZS50ZSA9IG5hbWVzKHR5cGUudGUpW3R5cGUudGUgPT0gbWF4KHR5cGUudGUpXQogICAgcmV0dXJuKHR5cGUudGVbMV0pCiAgfQp9KQp0YWJsZShub2Rlcy5jbnQkZmFtKQoKCiMgUmVkZWZpbmUgZWRnZXMgYnV0IHdpdGggbm9kZSBuYW1lcwppZHguZW5kZXMgPSAoZWRnZXNbLDFdICVpbiUgbm9kZXMkdGUpICYgKGVkZ2VzWywyXSAlaW4lIG5vZGVzJHRlKQpiLmdyYXBoID0gY2JpbmQobm9kZXNbZWRnZXNbaWR4LmVuZGVzLDFdLCAnbm9kZSddLG5vZGVzW2VkZ2VzW2lkeC5lbmRlcywyXSwgJ25vZGUnXSkKYi5ncmFwaCA9IHVuaXF1ZShiLmdyYXBoKQojIGIuZ3JhcGggPSBiLmdyYXBoW2IuZ3JhcGhbLDFdICE9IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaC51bmkgPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbLDJdLF0KYi5ncmFwaCA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gIT0gYi5ncmFwaFssMl0sXQoKbGVuZ3RoKHVuaXF1ZShjKGIuZ3JhcGhbLDFdLCBiLmdyYXBoWywyXSkpKQoKIyByZWR1Y2UgaW5kaXJlY3QgYXJyb3dzCmlkeC5yZW1vdmUgPSBjKCkKZm9yKGkuZWRnZSBpbiAxOm5yb3coYi5ncmFwaCkpewogIGlmKGkuZWRnZSAlJSAxMDAwID09IDApIHByaW50KGkuZWRnZSkKICB0bXAudG8gPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbaS5lZGdlLDFdLDJdCiAgdG1wLmZyb20gPSBiLmdyYXBoW2IuZ3JhcGhbLDJdID09IGIuZ3JhcGhbaS5lZGdlLDJdLDFdCiAgaWYobGVuZ3RoKGludGVyc2VjdCh0bXAudG8sIHRtcC5mcm9tKSkgPiAwKSBpZHgucmVtb3ZlID0gYyhpZHgucmVtb3ZlLCBpLmVkZ2UpCn0KaWR4LnJlbW92ZSA9IHVuaXF1ZShpZHgucmVtb3ZlKQpiLmdyYXBoID0gYi5ncmFwaFstaWR4LnJlbW92ZSxdCiMgYi5ncmFwaCA9IHJiaW5kKGIuZ3JhcGgsIGIuZ3JhcGgudW5pKQoKIyBQcmludCBncmFwaAoKZy5ub2Rlcy50eXBlID0gbm9kZXMuY250JHR5cGUKbmFtZXMoZy5ub2Rlcy50eXBlKSA9IG5vZGVzLmNudCRub2RlCmcubm9kZXMuY250ID0gbm9kZXMuY250JGNudApuYW1lcyhnLm5vZGVzLmNudCkgPSBub2Rlcy5jbnQkbm9kZQpnLm5vZGVzLmZhbSA9IG5vZGVzLmNudCRmYW0KbmFtZXMoZy5ub2Rlcy5mYW0pID0gbm9kZXMuY250JG5vZGUKCgpnLmNvbHMubmFtZXMgPSBjKCJub1RFIiwgImlzVEUiLCAiaGFzVEUiLCAiaGFzVEVwYXJ0IiwgImlzVEVwYXJ0IikKZy5jb2xzID0gYygnI0ZGRDk2NicsICcjRUI0NTVGJywgJyM3QjYwNzknLCAnIzNDOERBRCcsICcjNzlCNzczJykKbmFtZXMoZy5jb2xzKSA9IGcuY29scy5uYW1lcwoKCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpiLmdyYXBoLm5hbWVzID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0KQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnR5cGVbYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMpICsgZ3VpZGVzKHNpemUgPSBGKQpwCgojIHBhdGguZmlndXJlcyAgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3RlLycKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9hbGxfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3R5cGUucGRmJywgc2VwID0gJycpLCAKICAgIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgoKIyBzZXQuc2VlZCgyMCkKIyBwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJncmV5MzAiLCAKIyAgICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiMgICAgICAgICAgICAgY29sb3IgPSBjKCdURScsICdub1RFJylbKGcubm9kZXMudHlwZVtiLmdyYXBoLm5hbWVzXSA9PSAnbm9URScpKjErMV0sCiMgICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKIyAgICAgICAgICAgICAjIGFycm93LmdhcCA9IDAsIAojICAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiMgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoJ25vVEUnID0gJ2JsYWNrJywgJ1RFJyA9ICcjQUVDM0FFJykpICsgZ3VpZGVzKHNpemUgPSBGKQojIHAKIyAKIyAjIHBhdGguZmlndXJlcyAgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3RlLycKIyBwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2FsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAojICAgICB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCiMgcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKIyBkZXYub2ZmKCkKCmBgYAoKCiMjIyBDb2xvcmVkIGJ5IFRFIGZhbWlseQpgYGB7cn0KCmlmKGxlbmd0aChzZXRkaWZmKGcubm9kZXMuZmFtLCBuYW1lcyhmYW0ucGFsZXR0ZSkpKSE9MCkgc3RvcCgnbm90IGFsbCBmYW1pbGllcyBhcmUgZGVmaW5lZCcpCgpzZXQuc2VlZCgyMCkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiZ3JleTIwIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgIyBhcnJvdy5nYXAgPSAwLCAKICAgICAgICAgICAgIyBhcnJvdy5zaXplID0gMywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKcCA9IHAgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksIAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjUsICJjbSIpKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpCnAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19mYW1pbHkucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfY2x1c3RlcicsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX2ZhbWlseV9sZWdlbmQucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCnByaW50KHArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCmBgYAoKCiMjIyBOb2RlIHNpemUgZGlzdHJpYnV0aW9uCmBgYHtyfQpkZiA9IGRhdGEuZnJhbWUobm9kZSA9IHVuaXF1ZShub2RlcyRub2RlKSkKZGYkc2l6ZSA9IGcubm9kZXMuY250W2RmJG5vZGVdCmRmJGZhbSA9IGcubm9kZXMuZmFtW2RmJG5vZGVdCmRmJHR5cGUgPSBnLm5vZGVzLnR5cGVbZGYkbm9kZV0KCmZhbS5wYWxldHRlCgpwID0gZ2dwbG90KGRmLCBhZXMoeCA9IHR5cGUsIHkgPSBzaXplLCBjb2xvcj1mYW0pKSArCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIpICsKICBsYWJzKHggPSAiVHlwZSIsIHkgPSAiU2l6ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGZhbS5wYWxldHRlKSsKICB0aGVtZV9taW5pbWFsKCkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsKICBsYWJzKGNvbG9yID0gIlRFIGZhbWlseSIpICsgeGxhYignJykgKyB5bGFiKCdOb2RlIHNpemUgKE51bWJlciBvZiBzaW1pbGFyIFNWcyknKQpwCgoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9zaXplX2Rpc3RyaWJ1dGlvbi5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNi41LCBoZWlnaHQgPSA0KQpwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCmBgYAoKCgojIyMgU2VwYXJhdGVseSB2aXN1YWxpc2UgY29ubmVjdGVkIGNvbXBvbmVudHMKYGBge3J9CnRtcC5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChiLmdyYXBoKSwgZGlyZWN0ZWQgPSBUKQp0bXAuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeSh0bXAuZ3JhcGgpCnRtcC5jb21wIDwtIGlncmFwaDo6Y29tcG9uZW50cyh0bXAuZ3JhcGgpCgp0bXAuY250ID0gdGFibGUodG1wLmNvbXAkbWVtYmVyc2hpcCkKdG1wLmNudCA9IC1zb3J0KC10bXAuY250KQpoZWFkKHRtcC5jbnQpCgprID0gMQp0bXAuayA9IGFzLm51bWVyaWMobmFtZXModG1wLmNudClba10pCnRtcC5uYW1lcyA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApW3RtcC5jb21wJG1lbWJlcnNoaXAgPT0gdG1wLmtdCmIuZ3JhcGguc3ViID0gYi5ncmFwaFsoYi5ncmFwaFssMV0gJWluJSB0bXAubmFtZXMpICYgCiAgICAgICAgICAgICAgICAgICAgICAgIChiLmdyYXBoWywyXSAlaW4lIHRtcC5uYW1lcyksXQoKZy5wYXJ0LnN1Yi5iaWcgPC0gbmV0d29yayhiLmdyYXBoLnN1YiwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMuc3ViLmJpZyA9IG5ldHdvcmsudmVydGV4Lm5hbWVzKGcucGFydC5zdWIuYmlnKQoKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLmJpZywgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMudHlwZVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAogICAgICAgICAgICBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scykgKyBndWlkZXMoc2l6ZSA9IEYpCnAuYmlnLnR5cGUgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydC5zdWIuYmlnLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lcy5zdWIuYmlnXSwKICAgICAgICAgICAgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpCnAuYmlnLmNvbG9yID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgp0bXAuayA9IGFzLm51bWVyaWMobmFtZXModG1wLmNudClba10pCnRtcC5uYW1lcyA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApW3RtcC5jb21wJG1lbWJlcnNoaXAgIT0gdG1wLmtdCmIuZ3JhcGguc3ViID0gYi5ncmFwaFsoYi5ncmFwaFssMV0gJWluJSB0bXAubmFtZXMpICYgCiAgICAgICAgICAgICAgICAgICAgICAgIChiLmdyYXBoWywyXSAlaW4lIHRtcC5uYW1lcyksXQoKZy5wYXJ0LnN1Yi5zbWFsbCA8LSBuZXR3b3JrKGIuZ3JhcGguc3ViLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gVFJVRSkKYi5ncmFwaC5uYW1lcy5zdWIuc21hbGwgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQuc3ViLnNtYWxsKQoKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnR5cGVbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzKSArIGd1aWRlcyhzaXplID0gRikKcC5zbWFsbC50eXBlID1wICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKc2V0LnNlZWQoMjApCnAgPC0gZ2duZXQyKGcucGFydC5zdWIuc21hbGwsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sIAogICAgICAgICAgICBjb2xvciA9IGcubm9kZXMuZmFtW2IuZ3JhcGgubmFtZXMuc3ViLnNtYWxsXSwKICAgICAgICAgICAgIyBtb2RlID0gJ2thbWFkYWthd2FpJywKICAgICAgICAgICAgcGFsZXR0ZSA9IGZhbS5wYWxldHRlKSArIGd1aWRlcyhzaXplID0gRikKcC5zbWFsbC5jb2xvciA9IHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKCmBgYAoKIyMjIyBTYXZlCmBgYHtyfQojIHAuYmlnLnR5cGUKIyBwLmJpZy5jb2xvcgojIHAuc21hbGwudHlwZQojIHAuc21hbGwuY29sb3IKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX2JpZ19jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwLmJpZy50eXBlKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2JfYmlnX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19mYW1pbHkucGRmJywgc2VwID0gJycpLCAKICAgIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocC5iaWcuY29sb3IpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9zbWFsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfdHlwZS5wZGYnLCBzZXAgPSAnJyksIAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQpwcmludChwLnNtYWxsLnR5cGUpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX21vYl9zbWFsbF9jbHVzdGVyJywgcHJlZml4Lm1vZGVbc2luZ2xldG9uLm1vZGUrMV0gLCdfZmFtaWx5LnBkZicsIHNlcCA9ICcnKSwgCiAgICB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCnByaW50KHAuc21hbGwuY29sb3IpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKCgpgYGAKCgoKCiMjIyBSdW4gYnkgYWNjZXNzaW9ucwpgYGB7cn0KcGF0aC5maWd1cmVzLmFjYyA9ICcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfdGUvZmlndXJlc190ZWdyYXBoX2FjY2Vzc2lvbnMvJwpzdi5iaW4gPSByZWFkLnRhYmxlKCcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfc3Yvc3ZzX3NlX2Jpbl92MDMudHh0Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEYsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmBgYAoKCmBgYHtyfQojIGFjYyA9ICcxMDAwMicKCmZvcihhY2MgaW4gY29sbmFtZXMoc3YuYmluKSl7CiAgc3YuYWNjID0gcm93bmFtZXMoc3YuYmluKVtzdi5iaW5bLGFjY10gPT0gMV0KICByb3duYW1lcyhzdi5zZSkgPSBzdi5zZSRncgogIHN2LmFjYyA9IHN2LnNlW3N2LmFjYywgJ25hbWUnXQogIAogIHN2LmFjYyA9IGludGVyc2VjdChzdi5hY2MsIHJvd25hbWVzKG5vZGVzKSkKICBub2Rlcy5jbnQuYWNjID0gdGFibGUobm9kZXNbc3YuYWNjLCdub2RlJ10pCiAgCiAgCiAgc3YuYWxwaGEgPSByZXAoMCwgbGVuZ3RoKGIuZ3JhcGgubmFtZXMpKQogIG5hbWVzKHN2LmFscGhhKSA9IGIuZ3JhcGgubmFtZXMKICBzdi5hbHBoYVtuYW1lcyhzdi5hbHBoYSkgJWluJSBuYW1lcyhub2Rlcy5jbnQuYWNjKV0gPSAxCiAgCiAgIyBzZXQuc2VlZCgyMzkpCiAgIyBwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICMgICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICMgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzXSwKICAjICAgICAgICAgICAgIGFscGhhID0gc3YuYWxwaGEsCiAgIyAgICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICMgICAgICAgICAgICAgIyBhcnJvdy5nYXAgPSAwLCAKICAjICAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiAgIyAgICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICBzZXQuc2VlZCgyMCkKICBwIDwtIGdnbmV0MihnLnBhcnQuc3ViLnNtYWxsLCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5zbWFsbF0sCiAgICAgICAgICAgIGFscGhhID0gc3YuYWxwaGFbYi5ncmFwaC5uYW1lcy5zdWIuc21hbGxdLAogICAgICAgICAgICAjIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiAgcGRmKHBhc3RlKHBhdGguZmlndXJlcy5hY2MsICdncmFwaF90ZScsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX3NtYWxsX2FjY18nLGFjYywnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQogIHByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCiAgZGV2Lm9mZigpCiAgCiAgCiAgc2V0LnNlZWQoMjApCiAgcCA8LSBnZ25ldDIoZy5wYXJ0LnN1Yi5iaWcsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLCAKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLmZhbVtiLmdyYXBoLm5hbWVzLnN1Yi5iaWddLAogICAgICAgICAgICBhbHBoYSA9IHN2LmFscGhhW2IuZ3JhcGgubmFtZXMuc3ViLmJpZ10sCiAgICAgICAgICAgIG1vZGUgPSAna2FtYWRha2F3YWknLAogICAgICAgICAgICBwYWxldHRlID0gZmFtLnBhbGV0dGUpICsgZ3VpZGVzKHNpemUgPSBGKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiAgcGRmKHBhc3RlKHBhdGguZmlndXJlcy5hY2MsICdncmFwaF90ZScsIHByZWZpeC5tb2RlW3NpbmdsZXRvbi5tb2RlKzFdICwnX2JpZ19hY2NfJyxhY2MsJy5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKICBwcmludChwKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgogIGRldi5vZmYoKQoKfQoKcCAKCgoKYGBgCgoKYGBge3J9CnN2LmFubm90ID0gcmVhZC50YWJsZSgnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3N2L3N2c19hbm5vdGF0aW9uX3YwMy50eHQnLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKcm93bmFtZXMoc3YuYW5ub3QpID0gc3YuYW5ub3QkZ3IKaGVhZChzdi5hbm5vdCkKCnN2LmFubm90W2V4dHJhY3RlZF92YWx1ZXMsXQoKYGBgCgoKCgojIFN0b3AKYGBge3J9CnN0b3AoKQpgYGAKCgojIEJpZyBURS1ub2RlcwpgYGB7cn0Kbi5hbW91bnQgPSAyMAoKZy5wYXJ0IDwtIG5ldHdvcmsoYi5ncmFwaCwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCgpzaXplLmJpZyA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdCmFscGhhLmJpZyA9IHJlcCgxLCBsZW5ndGgoYi5ncmFwaC5uYW1lcykpCm5hbWVzKGFscGhhLmJpZykgPSBiLmdyYXBoLm5hbWVzCmFscGhhLmJpZ1tzaXplLmJpZyA8IG4uYW1vdW50XSA9IDAKCnN1bShzaXplLmJpZyA+PSBuLmFtb3VudCkKCnNldC5zZWVkKDIwKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICBub2RlLnNpemUgPSBzaXplLmJpZywgCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5mYW1bYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIGFscGhhPSBhbHBoYS5iaWcsCiAgICAgICAgICAgICMgbW9kZSA9ICdrYW1hZGFrYXdhaScsCiAgICAgICAgICAgICMgYXJyb3cuZ2FwID0gMCwgCiAgICAgICAgICAgICMgYXJyb3cuc2l6ZSA9IDMsCiAgICAgICAgICAgIHBhbGV0dGUgPSBmYW0ucGFsZXR0ZSkgKyBndWlkZXMoc2l6ZSA9IEYpICsgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX3NtYWxsX2NsdXN0ZXInLCBwcmVmaXgubW9kZVtzaW5nbGV0b24ubW9kZSsxXSAsJ19mYW1pbHlfYW1vdW50LnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwcmludChwKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgoKCgpgYGAKCiMjIFdoaWNoIGZhbWlsaWVzIHNwZWNpZmljYWxseSwgYW5kIGlzIHRoZSByYXRlIG9mIGluc2VydGlvbiBpcyBkaWZmZXJlbnQ/CmNvbXBhcmUgbnVtYmVyIG9mIGluc2VydGlvbnMgd2l0aCB0aGUgdG90YWwgbnVtYmVyIG9mIFRFIGxvYWQKYGBge3J9CgpiaWcuZmFtaWxpZXMgPSBkYXRhLmZyYW1lKG5vZGUgPSAgbmFtZXMoc2l6ZS5iaWcpW3NpemUuYmlnID49IG4uYW1vdW50XSkKYmlnLmZhbWlsaWVzJHNpemUgPSBzaXplLmJpZ1tiaWcuZmFtaWxpZXMkbm9kZV0KYmlnLmZhbWlsaWVzJGZhbSA9IGcubm9kZXMuZmFtW2JpZy5mYW1pbGllcyRub2RlXQpiaWcuZmFtaWxpZXMgPSBiaWcuZmFtaWxpZXNbb3JkZXIoLWJpZy5mYW1pbGllcyRzaXplKSxdCnJvd25hbWVzKGJpZy5mYW1pbGllcykgPSBOVUxMCgpub2RlLmJpZyA9IG5vZGVzW25vZGVzJG5vZGUgJWluJSBiaWcuZmFtaWxpZXMkbm9kZSxdCgp2ID0gcmVhZC50YWJsZShwYXN0ZShwYXRoLndvcmssICdibGFzdF9zdl9vbl90ZXMudHh0Jywgc2VwID0gJycpKQp2ID0gdlt2JFYxICVpbiUgbm9kZS5iaWckdGUsXQoKCnBvcy5sZW4xID0gMgpwb3MubGVuMiA9IDUKdjEubGVuID0gc2FwcGx5KHVuaXF1ZSh2JFYxKSwgZnVuY3Rpb24ocykgYXMubnVtZXJpYyhzdHJzcGxpdChzLCdcXHwnKVtbMV1dW3Bvcy5sZW4xXSkpCnY4LmxlbiA9IHNhcHBseSh1bmlxdWUodiRWOCksIGZ1bmN0aW9uKHMpIGFzLm51bWVyaWMoc3Ryc3BsaXQocywnXFx8JylbWzFdXVtwb3MubGVuMl0pKQp2LmxlbiA9IGModjEubGVuLCB2OC5sZW4pCgp2LnNpbSA9IGZpbmROZXN0ZWRuZXNzKHYsIHVzZS5zdHJhbmQgPSBGKQoKdi5zaW0gPSBmaW5kTmVzdGVkbmVzcyh2LCB1c2Uuc3RyYW5kID0gRikKdi5zaW0kcDEgPSB2LnNpbSRDMSAvIHYubGVuW3Yuc2ltJFYxXQp2LnNpbSRwOCA9IHYuc2ltJEM4IC8gdi5sZW5bdi5zaW0kVjhdCnYuc2ltJHAxLmluOCA9IHYuc2ltJEMxIC8gdi5sZW5bdi5zaW0kVjhdCnYuc2ltJHA4LmluMSA9IHYuc2ltJEM4IC8gdi5sZW5bdi5zaW0kVjFdCgpub2RlLmJpZyRzdWJmYW0gPSAnJwpmb3Ioc3YubmFtZSBpbiB1bmlxdWUodi5zaW0kVjEpKXsKICB2LnRtcCA9IHYuc2ltW3Yuc2ltJFYxID09IHN2Lm5hbWUsXQogIHMgPSB2LnRtcCRWOFt3aGljaC5tYXgodi50bXAkcDEpXQogIHMgPSBzdHJzcGxpdChzLCAnXFx8JylbWzFdXVs4XQogIG5vZGUuYmlnW3N2Lm5hbWUsICdzdWJmYW0nXSA9IHMKfQoKCnggPSB0YXBwbHkobm9kZS5iaWckc3ViZmFtLCBub2RlLmJpZyRub2RlLCBmdW5jdGlvbih4KXsKICBjbnQgPSB0YWJsZSh4KQogIHggPSBuYW1lcyhjbnQpW2NudCA9PSBtYXgoY250KV0KICByZXR1cm4ocGFzdGUwKHgsIGNvbGxhcHNlID0gICcsJykpCn0pCgpiaWcuZmFtaWxpZXMkc3ViZmFtID0geFtiaWcuZmFtaWxpZXMkbm9kZV0KCgpgYGAKCgoKIyBuby1URSBTVgojIyBDb25zdHJ1Y3QKYGBge3J9CnN2LnNlID0gcmVhZFJEUyhwYXN0ZShwYXRoLnN2cywgJ3N2X3NlLnJkcycsIHNlcCA9ICcnKSkKc2ltLmN1dG9mZiA9IDAuODUKCgpzdi5zZS5uby50ZSA9IHN2LnNlJG5hbWVbKHN2LnNlJHRlID09ICdub1RFJykgJiAoc3Yuc2UkbGVuID4gNTApXQoKYmwuZmlsZSA9IHBhc3RlKHBhdGgud29yaywnc3ZfYmlnX29uX2JpZy50eHQnLCBzZXAgPSAnJykKYmwuc3YgPSByZWFkLnRhYmxlKGJsLmZpbGUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpibC5zdiA9IGJsLnN2W2JsLnN2JFYxICE9IGJsLnN2JFY4LF0KCiMgcmVtb3ZlIGhhdmluZyBURXMKYmwuc3YgPSBibC5zdltibC5zdiRWMSAlaW4lIHN2LnNlLm5vLnRlLCBdCmJsLnN2ID0gYmwuc3ZbYmwuc3YkVjggJWluJSBzdi5zZS5uby50ZSwgXQoKcG9zLmxlbjEgPSAyCnN2LmxlbiA9IHNhcHBseSh1bmlxdWUoYyhibC5zdiRWMSwgYmwuc3YkVjgpKSwgZnVuY3Rpb24ocykgYXMubnVtZXJpYyhzdHJzcGxpdChzLCdcXHwnKVtbMV1dW3Bvcy5sZW4xXSkpCmJsLnN2JGxlbjEgPSBzdi5sZW5bYmwuc3YkVjFdCmJsLnN2JGxlbjggPSBzdi5sZW5bYmwuc3YkVjhdCm1heC5sZW4gPSAyMDAwMApibC5zdiA9IGJsLnN2WyhibC5zdiRsZW4xIDw9IG1heC5sZW4pICYgKGJsLnN2JGxlbjggPD0gbWF4LmxlbiksXQpibC5zdiRwMSA9IChibC5zdiRWMyAtIGJsLnN2JFYyICsgMSkgLyBibC5zdiRsZW4xCmJsLnN2JHA4ID0gKGFicyhibC5zdiRWNSAtIGJsLnN2JFY0KSArIDEpIC8gYmwuc3YkbGVuOApibC5zdiRjb21iID0gYXMuZmFjdG9yKHBhc3RlKGJsLnN2JFYxLCBibC5zdiRWOCwgc2VwID0gJ3x8JykpCgppZHgubXV0dWFsID0gKGJsLnN2JHAxID49IHNpbS5jdXRvZmYpICYgKGJsLnN2JHA4ID49IHNpbS5jdXRvZmYpCiMgVGhlcmUgaXMgYSBiaWcgZGlzY3Vzc2lvbiBpbiBteSBoZWFkLCB3aGV0aGVyIGl0IHNob3VsZCBiZSAnJicgb3IgJ3wnCiMgSWYgaXQncyBub3QgLHV0dWFsLCB0aGVuIG1heWJlIHdpdGggc29tZXRoaW5nIGVsc2UgaXQgd2lsbCBjb25zdHJ1Y3QgYSBtdXR1YWwgcmVsYXRpb24sIAojIHNvIHdlIHNob3VsZCByZW1haW4gZm9yIHRoZSBhbmFseXNpcyBvZiBuZXN0ZWRuZXNzIGFsbCBwYXJ0aWFsIGluY2x1c2lvbnMKc3YubXV0dWFsID0gYmwuc3ZbaWR4Lm11dHVhbCwgXQp2ID0gYmwuc3ZbIWlkeC5tdXR1YWwsIF0KdiA9IHZbISh2JGNvbWIgJWluJSBzdi5tdXR1YWwkY29tYiksXQoKIyBBdCBzb21lIHBvaW50IGl0IHdhcyBhIHN0ZXAgdG8gcmVtYWluIG9ubHkgdGhvc2UgaW5zdGFuY2VzIHdoaWNoIGFyZSBub3QgInVuaXF1ZSIgaW4gY29tYmluYXRpb25zCiMgYnV0IEkgdGhpbmsgaXQncyBub3QgY29ycmVjdCBoZXJlCgpzdi5zaW0gPSBmaW5kTmVzdGVkbmVzcyh2LCB1c2Uuc3RyYW5kID0gVCkKc3Yuc2ltJHAxID0gc3Yuc2ltJEMxIC8gc3YubGVuW3N2LnNpbSRWMV0Kc3Yuc2ltJHA4ID0gc3Yuc2ltJEM4IC8gc3YubGVuW3N2LnNpbSRWOF0KCiMgaGVyZSAgd2Ugc2hvdWxkIGZpbmFsbHkgdXNlICd8Jywgbm90ICcmJwpzdi5uZXN0ZWQgPSBzdi5zaW1bKHN2LnNpbSRwMSA+PSBzaW0uY3V0b2ZmKSB8IChzdi5zaW0kcDggPj0gc2ltLmN1dG9mZikgLF0KCiMgQ3JlYXRlIHByZS1kYXRhIGZvciBkZWZpbmluZyBlZGdlcwpjb21tb24ubmFtZXMgPSBpbnRlcnNlY3QoY29sbmFtZXMoc3YubXV0dWFsKSwgY29sbmFtZXMoc3YubmVzdGVkKSkKc3Yub3ZlcmFsbCA9IHJiaW5kKHN2Lm11dHVhbFssY29tbW9uLm5hbWVzXSwgc3YubmVzdGVkWyxjb21tb24ubmFtZXNdKQpzdi5vdmVyYWxsJGdyb3VwID0gKHN2Lm92ZXJhbGwkcDEgPj0gc2ltLmN1dG9mZikgKiAxICsgKHN2Lm92ZXJhbGwkcDggPj0gc2ltLmN1dG9mZikgKiAyCmlkeDEgPSBzdi5vdmVyYWxsJGdyb3VwICE9IDIgICMgVjEgaW4gVjgKaWR4MiA9IHN2Lm92ZXJhbGwkZ3JvdXAgIT0gMSAgIyBWOCBpbiBWMQoKCiMgRWRnZXMgCnN2LmVkZ2VzID0gcmJpbmQoY2JpbmQoc3Yub3ZlcmFsbCRWMVtpZHgxXSwgc3Yub3ZlcmFsbCRWOFtpZHgxXSksCiAgICAgICAgICAgICAgICAgY2JpbmQoc3Yub3ZlcmFsbCRWOFtpZHgyXSwgc3Yub3ZlcmFsbCRWMVtpZHgyXSkpCgoKc3YuZ3JhcGggPC0gaWdyYXBoOjptYWtlX2dyYXBoKHQoc3YuZWRnZXMpLCBkaXJlY3RlZCA9IFQpCnN2LmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkoc3YuZ3JhcGgpCnN2LmdyYXBoY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHMoc3YuZ3JhcGgpCgpzdi5tZW1iID0gZGF0YS5mcmFtZShtZW1iID0gc3YuZ3JhcGhjb21wJG1lbWJlcnNoaXApCnN2Lm1lbWIkbmFtZSA9IHJvd25hbWVzKHN2Lm1lbWIpCnJvd25hbWVzKHN2Lm1lbWIpID0gTlVMTApyb3duYW1lcyhzdi5zZSkgPSBzdi5zZSRuYW1lCnN2Lm1lbWIkdGUgPSBzdi5zZVtzdi5tZW1iJG5hbWUsICd0ZSddCnN2Lm1lbWIkY292ZXIgPSBzdi5zZVtzdi5tZW1iJG5hbWUsICdjb3ZlciddIC8gc3Yuc2Vbc3YubWVtYiRuYW1lLCAnbGVuJ10Kc3YubWVtYiRsZW4gPSBzdi5sZW5bc3YubWVtYiRuYW1lXQpgYGAKCiMjIFBsb3QgYWxsCmBgYHtyfQpnLnBhcnQgPC0gbmV0d29yayhzdi5lZGdlcywgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCmIuZ3JhcGgubmFtZXMgPSBuZXR3b3JrLnZlcnRleC5uYW1lcyhnLnBhcnQpCgpzZXQuc2VlZCgyMzkpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgICMgbm9kZS5zaXplID0gZy5ub2Rlcy5jbnRbYi5ncmFwaC5uYW1lc10sIAogICAgICAgICAgICAjIGNvbG9yID0gZy5ub2Rlcy50eXBlW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICAjIHBhbGV0dGUgPSBnLmNvbHMKICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikKcCAKCmBgYAoKIyMgUGxvdCB3aXRoIGNvbG9ycwpgYGB7cn0Kc3YucHJvdC5pbml0ID0gcmVhZFJEUyhwYXN0ZShwYXRoLndvcmssICdzdl9wcm90ZWluc19ub190ZV9ibGFzdC5yZHMnLCBzZXAgPSAnJykpCnN2LnByb3QuaW5pdCRuYW1lID0gc2FwcGx5KHN2LnByb3QuaW5pdCRYMSwgZnVuY3Rpb24ocyl7CiAgcyA9IHBhc3RlMChzdHJzcGxpdChzLCAnXFx8JylbWzFdXVsxOjJdLCBjb2xsYXBzZSA9ICd8JykKICByZXR1cm4ocykKfSkKc3YucHJvdCA9IHN2LnByb3QuaW5pdFtzdi5wcm90LmluaXQkcHJvdCA9PSAxLF0Kc3YucHJvdFssMl0gPSB0b2xvd2VyKHN2LnByb3RbLDJdKQoKdHlwZXMgPSBjKCdkaXNlYXNlJywgJ3JlcGVhdCcsICdyZWNlcHRvcicsICAnemluYycsICd0cmFuc2NyaXB0YXNlJywgJ3JldmVyc2UnLCAndHJhbnNwb3MnKQpmb3IoaS50eXBlIGluIDE6bGVuZ3RoKHR5cGVzKSl7CiAgc3YucHJvdFssdHlwZXNbaS50eXBlXV0gPSAoZ3JlcGwodHlwZXNbaS50eXBlXSwgc3YucHJvdFssMl0pKSAqIDEKfQpzdi5wcm90JHR5cGUgPSByb3dTdW1zKHN2LnByb3RbLHR5cGVzXSkKdGFibGUoc3YucHJvdCR0eXBlKQoKCgpzdi5tZW1iJHByb3QgPSAnbm8gcHJvdCcKc3YubWVtYiRwcm90W3N2Lm1lbWIkbmFtZSAlaW4lIHN2LnByb3QuaW5pdCRuYW1lXSA9ICd1bmRlZmluZWQgcHJvdCcKc3YubWVtYiRwcm90W3N2Lm1lbWIkbmFtZSAlaW4lIHN2LnByb3QkbmFtZV0gPSAnZGVmaW5lZCBwcm90Jwpmb3IodHlwZSBpbiB0eXBlcyl7CiAgc3YubWVtYiRwcm90W3N2Lm1lbWIkbmFtZSAlaW4lIHN2LnByb3QkbmFtZVtzdi5wcm90Wyx0eXBlXSA9PSAxXV0gPSB0eXBlCn0KCmcubm9kZXMucHJvdCA9IHN2Lm1lbWIkcHJvdApnLm5vZGVzLnByb3RbZy5ub2Rlcy5wcm90ID09ICdkaXNlYXNlJ10gPSAnZGVmaW5lZCBwcm90JwpuYW1lcyhnLm5vZGVzLnByb3QpID0gc3YubWVtYiRuYW1lCgpnLmNvbHMgPSBkaXNjcmV0ZV9yYWluYm93KGxlbmd0aCh1bmlxdWUoc3YubWVtYiRwcm90KSkpCm5hbWVzKGcuY29scykgPSB1bmlxdWUoc3YubWVtYiRwcm90KQojIG5hbWVzKGcuY29scykgPSBjKCdubyBwcm90JywgInVuZGVmaW5lZCBwcm90IiwgInJldmVyc2UiLCAidHJhbnNwb3MiLCJyZXBlYXQiLCJ6aW5jIiwgInJlY2VwdG9yIiwiZGVmaW5lZCBwcm90IikKCmcuY29sc1snZGlzZWFzZSddID0gJ2JsYWNrJwojIGcuY29sc1snZGVmaW5lZCBwcm90J10gPSAnI0YwQjg2RScKIyBnLmNvbHNbJ3JlcGVhdCddID0gJyNDQUUwQUInCiMgZy5jb2xzWyd6aW5rJ10gPSAnIzFBNUQxQScKIyBnLmNvbHNbJ3JlY2VwdG9yJ10gPSAnI0VEN0I3QicKCmcuY29sc1snemluYyddID0gJyNGNDUwNTAnCmcuY29sc1sncmVwZWF0J10gPSAnI0VBOTA2QycKZy5jb2xzWydyZWNlcHRvciddID0gJyNGN0QwNjAnCgpnLmNvbHNbJ3JldmVyc2UnXSA9ICcjOThEOEFBJwpnLmNvbHNbJ2RlZmluZWQgcHJvdCddID0gJyM3QkFGREUnCgoKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpICsgY29vcmRfZml4ZWQocmF0aW8gPSAxKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGcuY29scywgCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygidHJhbnNwb3MiLCJyZXZlcnNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyZXBlYXQiLCJ6aW5jIiwicmVjZXB0b3IiLCAiZGVmaW5lZCBwcm90IiwgInVuZGVmaW5lZCBwcm90IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJubyBwcm90IiksIAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ1Byb3RlaW4ga2V5LXdvcmQ6JykgKyB0aGVtZShsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMSwgMCkpCnAgPSBwKyB0aGVtZShsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMC41LCAiY20iKSkKcAoKcGRmKHBhc3RlKHBhdGguZmlndXJlcywgJ2dyYXBoX25ld19hbGwucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKIyAKIyBjbnQgPSB0YWJsZShnLm5vZGVzLnByb3QpCiMgY250ID0gYyhzdW0oY250W2MoInRyYW5zcG9zIiwicmV2ZXJzZSIsInJlcGVhdCIsInppbmMiKV0pLCBzdW0oY250W2MoInJlY2VwdG9yIiwiZGVmaW5lZCBwcm90IildKSwKIyAgICAgICAgIGNudFsidW5kZWZpbmVkIHByb3QiXSwgY250WyJubyBwcm90Il0pCgpgYGAKCiMjIFR5cGVzIG9mIHRoZSBjb21wb25lbnQKCgpgYGB7cn0KCnN2LmdyYXBoIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KHN2LmVkZ2VzKSwgZGlyZWN0ZWQgPSBUKQpzdi5ncmFwaCA8LSBpZ3JhcGg6OnNpbXBsaWZ5KHN2LmdyYXBoKQpzdi5ncmFwaGNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHN2LmdyYXBoKQoKc3YuY29tcC5tZW1iZXIgPSBzdi5ncmFwaGNvbXAkbWVtYmVyc2hpcAoKcy50YWdzID0gYygidHJhbnNwb3MiLCJyZXZlcnNlIiwicmVwZWF0IiwiemluYyIsICJyZWNlcHRvciIsImRlZmluZWQgcHJvdCIsICJ1bmRlZmluZWQgcHJvdCIsICdubyBwcm90JykKcy50YWdzMCA9IHJlcCgnJywgbGVuZ3RoKHMudGFncykpCnMudGFnczBbMTo0XSA9ICdURS1saWtlJwpzLnRhZ3MwWzU6Nl0gPSAnS25vd24gUHJvdGVpbnMnCnMudGFnczBbN10gPSAnVW5kZWYuIFByb3RlaW5zJwpzLnRhZ3MwWzhdID0gJ05vIFByb3RlaW5zJwpuYW1lcyhzLnRhZ3MwKSA9IHMudGFncwoKY29tcC50YWdzID0gcmVwKCcnLCBsZW5ndGgodW5pcXVlKHN2LmNvbXAubWVtYmVyKSkpCmZvcihzLnRhZyBpbiBzLnRhZ3MpewogIHRtcC50YWdzID0gdW5pcXVlKHN2LmNvbXAubWVtYmVyW25hbWVzKGcubm9kZXMucHJvdClbZy5ub2Rlcy5wcm90ID09IHMudGFnXV0pCiAgY29tcC50YWdzW3RtcC50YWdzXVtjb21wLnRhZ3NbdG1wLnRhZ3NdID09ICcnXSA9IHMudGFnCn0KY29tcC50YWdzW2NvbXAudGFncyA9PSAnJ10gPSAnbm8gcHJvdCcKY29tcC50YWdzID0gZGF0YS5mcmFtZSh0YWJsZShjb21wLnRhZ3MpKQpjb2xuYW1lcyhjb21wLnRhZ3MpID0gYygndGFnMScsICdmcmVxJykKY29tcC50YWdzJHRhZzEgPSBmYWN0b3IoY29tcC50YWdzJHRhZzEsIGxldmVscyA9IHMudGFncykKY29tcC50YWdzID0gY29tcC50YWdzW29yZGVyKGNvbXAudGFncyR0YWcxKSxdCgpjb21wLnRhZ3MkdGFnMCA9IHMudGFnczBbY29tcC50YWdzJHRhZzFdCmNvbXAudGFncyR0YWcwID0gZmFjdG9yKGNvbXAudGFncyR0YWcwLCBsZXZlbHMgPSB1bmlxdWUocy50YWdzMCkpCgp5LnRpY2tzID0gdGFwcGx5KGNvbXAudGFncyRmcmVxLCBjb21wLnRhZ3MkdGFnMCwgc3VtKQp5LnRpY2tzID0geS50aWNrc1shaXMubmEoeS50aWNrcyldCgp5eSA9IHN1bSh5LnRpY2tzKSAtIGN1bXN1bSh5LnRpY2tzKSArIHkudGlja3MvMgoKY29tcC50YWdzJHltaW4gPC0gYygwLCBjdW1zdW0oY29tcC50YWdzJGZyZXEpWy1sZW5ndGgoY29tcC50YWdzJGZyZXEpXSkKY29tcC50YWdzJHltYXggPC0gY3Vtc3VtKGNvbXAudGFncyRmcmVxKQoKeC5zdGVwID0gcmVwKDAsIDgpCm4uc3RlcCA9IDEwCnguc3RlcFtjKDUsNyw4KV0gPSBuLnN0ZXAKeC5zdGVwID0gY3Vtc3VtKHguc3RlcCkKCmNvbXAudGFncyR5bWluID0gY29tcC50YWdzJHltaW4gKyB4LnN0ZXAKY29tcC50YWdzJHltYXggPSBjb21wLnRhZ3MkeW1heCArIHguc3RlcAoKeS5taW4gPSB0YXBwbHkoY29tcC50YWdzJHltaW4sIGNvbXAudGFncyR0YWcwLCBtaW4pCnkubWF4ID0gdGFwcGx5KGNvbXAudGFncyR5bWF4LCBjb21wLnRhZ3MkdGFnMCwgbWF4KQp5LnZhbCA9ICh5Lm1heCArIHkubWluKSAvIDIKeS5jbnQgPSB0YXBwbHkoY29tcC50YWdzJGZyZXEsIGNvbXAudGFncyR0YWcwLCBzdW0pCgpkZi50ZXh0ID0gZGF0YS5mcmFtZSh5Lm1pbiA9IHkubWluLCB5Lm1heCA9IHkubWF4LCB5LnZhbCA9IHkudmFsLCB5LmNudCA9IHkuY250LCBsYWJlbCA9IG5hbWVzKHkudmFsKSkKZGYudGV4dCRhbmdsZXMgPC0gMzYwIC0gKGRmLnRleHQkeS52YWwgLyAobWF4KGNvbXAudGFncyR5bWF4KSArIG4uc3RlcCkpICogMzYwIApkZi50ZXh0JGFuZ2xlc1syOjNdID0gMTgwICsgZGYudGV4dCRhbmdsZXNbMjozXQoKcCA9IGdncGxvdChjb21wLnRhZ3MsIGFlcyh4ID0gMCwgeSA9IGZyZXEsIGZpbGwgPSB0YWcxKSkgKwogICBnZW9tX3JlY3QoYWVzKHhtaW4gPSAtMC41LCB4bWF4ID0gMC41LCB5bWluID0geW1pbiwgeW1heCA9IHltYXgpKSArCiAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSArCiAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGcuY29scy5wbHVzKSArIHlsaW0oMCwgbWF4KGNvbXAudGFncyR5bWF4KSArIG4uc3RlcCkgKwogICB0aGVtZV92b2lkKCkgKyB4bGltKC0xLjUsIDAuNykgKyAKICAgZ2VvbV90ZXh0KGRhdGE9ZGYudGV4dCwgYWVzKHggPSAwLjcsIHkgPSB5LnZhbCwgbGFiZWwgPSBwYXN0ZShsYWJlbCwgeS5jbnQsIHNlcCA9ICc6ICcpKSwgCiAgICAgICAgICAgICBhbmdsZSA9IGRmLnRleHQkYW5nbGVzLCBpbmhlcml0LmFlcyA9IEZBTFNFKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAtMS41LCB5ID0gMCwgbGFiZWwgPSBwYXN0ZSgnVG90YWwnLHN1bShjb21wLnRhZ3MkZnJlcSksJ1xuIGNvbm5lY3RlZCBcbmNvbXBvbmVudHMnKSkgCgpwCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbmV3X3BpZV9jaGFydC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gMy4xLCBoZWlnaHQgPSAzLjEpCnByaW50KHApICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGCmRldi5vZmYoKQoKYGBgCgoKCiMjIyMgSSBkb24ndCBrbm93CmBgYHtyfQpzdi5zZSRmcmVxID0gc3Yuc2UkZnJlcS5tYXgKbi5jdXRvZmYgPSAzCm4gPSAyOApzdi5zZSRzaW4gPSAnaW5kZWwnCnN2LnNlJHNpbltzdi5zZSRmcmVxID49IChuIC0gbi5jdXRvZmYpXSA9ICdkZWxldGlvbicKc3Yuc2Ukc2luW3N2LnNlJGZyZXEgPD0gbi5jdXRvZmZdID0gJ2luc2VydGlvbicKCgpnLm5vZGVzLnByb3Quc2luID0gZy5ub2Rlcy5wcm90Cmcubm9kZXMucHJvdC5zaW5bbmFtZXMoZy5ub2Rlcy5wcm90LnNpbikgJWluJSBzdi5zZSRuYW1lW3N2LnNlJHNpbiAhPSAnaW5zZXJ0aW9uJ10gXSA9ICduYScKZy5jb2xzWyduYSddID0gJ3doaXRlJwoKCgoKc2V0LnNlZWQoMjM5KQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRiwgZWRnZS5jb2xvciA9ICJibGFjayIsIAogICAgICAgICAgICAjIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gMSwKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnByb3Quc2luW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICBwYWxldHRlID0gZy5jb2xzLAogICAgICAgICAgICAjIG1vZGUgPSAia2FtYWRha2F3YWkiCiAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpCnAgCgojIAojIHBhdGguZmlndXJlcyAgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrX3RlLycKIyBwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfc3Zfbm90ZV9pbnNlcnRpb24ucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCiMgcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKIyBkZXYub2ZmKCkKCgphbHBoYS5lZHRhID0gcmVwKDEsIGxlbmd0aChiLmdyYXBoLm5hbWVzKSkKbmFtZXMoYWxwaGEuZWR0YSkgPSBiLmdyYXBoLm5hbWVzCgpzdi5hbm5vdC5hZHRhID0gcm93U3Vtcyhzdi5hbm5vdFssMTE6bmNvbChzdi5hbm5vdCldID4gMC43KSA+IDAKc3YuYW5ub3QuYWR0YSA9IHN2LmFubm90LmFkdGFbc3Yuc2UkZ3JdCm5hbWVzKHN2LmFubm90LmFkdGEpID0gc3Yuc2UkbmFtZQpzdi5hbm5vdC5hZHRhID0gc3YuYW5ub3QuYWR0YVtzdi5hbm5vdC5hZHRhXQphbHBoYS5lZHRhW25hbWVzKGFscGhhLmVkdGEpICVpbiUgbmFtZXMoc3YuYW5ub3QuYWR0YSldID0gMAoKCnNldC5zZWVkKDIzOSkKcCA8LSBnZ25ldDIoZy5wYXJ0LCBsYWJlbCA9IEYsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgIyBub2RlLnNpemUgPSBnLm5vZGVzLmNudFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIG5vZGUuc2l6ZSA9IDEsCiAgICAgICAgICAgIGFscGhhPTEtYWxwaGEuZWR0YSwKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnByb3RbYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMsCiAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikKcCAKCgpwZGYocGFzdGUocGF0aC5maWd1cmVzLCAnZ3JhcGhfbW9iX25vdGVfZWR0YS5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKcHJpbnQocCkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpwYXRoLmZpZ3VyZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS8nCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9tb2Jfbm90ZV9lZHRhX25vX2xlZ2VuZC5wZGYnLCBzZXAgPSAnJyksIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSkKcHJpbnQocCsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkgICAgICMgUGxvdCAxIC0tPiBpbiB0aGUgZmlyc3QgcGFnZSBvZiBQREYKZGV2Lm9mZigpCgpgYGAKCgojIyBQbG90IHdpdGggY29tcG9uZW50IElECmBgYHtyfQoKCnRtcC5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChzdi5lZGdlcyksIGRpcmVjdGVkID0gVCkKdG1wLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodG1wLmdyYXBoKQp0bXAuY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModG1wLmdyYXBoKQoKc2l6ZS5saW1pdCA9IDUKY29tcC5pZCA9IGFzLmNoYXJhY3Rlcih0bXAuY29tcCRtZW1iZXJzaGlwKQpuYW1lcyhjb21wLmlkKSA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApCmNvbXAuaWRbdG1wLmNvbXAkY3NpemVbdG1wLmNvbXAkbWVtYmVyc2hpcF0gPCBzaXplLmxpbWl0XSA9ICcnCgpuYW1lcy50ZSA9IG5hbWVzKGcubm9kZXMucHJvdClbZy5ub2Rlcy5wcm90ICVpbiUgYygndHJhbnNwb3MnLCAncmV2ZXJzZScpXQoKY29tcC5pZFshKG5hbWVzKGNvbXAuaWQpICVpbiUgbmFtZXMudGUpXSA9ICcnCgpjb21wLmlkW2R1cGxpY2F0ZWQoY29tcC5pZCldID0gJycKCgpjb21wLnJlbWFpbiA9IGFzLm51bWVyaWMoY29tcC5pZFtjb21wLmlkICE9ICcnXSkKYWxwaGEgPSByZXAoMCwgbGVuZ3RoKGIuZ3JhcGgubmFtZXMpKQpuYW1lcyhhbHBoYSkgPSBuYW1lcyh0bXAuY29tcCRtZW1iZXJzaGlwKQphbHBoYVt0bXAuY29tcCRtZW1iZXJzaGlwICVpbiUgY29tcC5yZW1haW5dID0gMQoKc2V0LnNlZWQoMjM5KQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gY29tcC5pZFtiLmdyYXBoLm5hbWVzXSwgCiAgICAgICAgICAgIGxhYmVsLmNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgICAgIGVkZ2UuY29sb3IgPSAiZ3JleSIsIAogICAgICAgICAgICBhbHBoYSA9IGFscGhhW2IuZ3JhcGgubmFtZXNdLAogICAgICAgICAgICAjIG5vZGUuc2l6ZSA9IGcubm9kZXMuY250W2IuZ3JhcGgubmFtZXNdLCAKICAgICAgICAgICAgbm9kZS5zaXplID0gMSwKICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnByb3RbYi5ncmFwaC5uYW1lc10sCiAgICAgICAgICAgIHBhbGV0dGUgPSBnLmNvbHMsCiAgICAgICAgICAgICMgbW9kZSA9ICJrYW1hZGFrYXdhaSIKICAgICAgICAgICAgKSArIGd1aWRlcyhzaXplID0gRikKcCAKCgpwYXRoLmZpZ3VyZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS8nCnBkZihwYXN0ZShwYXRoLmZpZ3VyZXMsICdncmFwaF9zdl9ub3RlX251bWJlcnMucGRmJywgc2VwID0gJycpLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCnByaW50KHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgpkZXYub2ZmKCkKCgoKIyBPcmRlciBvZiBjb21wb25lbnRzCmNudCA9IHRhYmxlKHRtcC5jb21wJG1lbWJlcnNoaXBbdG1wLmNvbXAkbWVtYmVyc2hpcCAlaW4lIGNvbXAucmVtYWluXSkKY250ID0gY250W29yZGVyKC1jbnQpXQoKYGBgCgojIyBDTlYKYGBge3J9CgpjbnYgPSByZWFkUkRTKCcvVm9sdW1lcy9TYW1zdW5nX1Q1L3ZpZW5uL3dvcmtfc3Yvc2ltaWxhcl9jbnZfc3Zfb25fYWNjZXNzaW9uc19jdW1fMC45LnJkcycpCgpgYGAKCiMjIFBsb3Qgb25lIHNwZWNpZmljIG5ldHdvcmsKYGBge3J9CgpwYXRoLmZpZ3VyZXMuZXhhbXBsZXMgID0gJy9Wb2x1bWVzL1NhbXN1bmdfVDUvdmllbm4vd29ya190ZS9leGFtcGxlcy8nCgojIAojIHRtcC5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChzdi5lZGdlcyksIGRpcmVjdGVkID0gVCkKIyB0bXAuZ3JhcGggPC0gaWdyYXBoOjpzaW1wbGlmeSh0bXAuZ3JhcGgpCiMgdG1wLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRtcC5ncmFwaCkKIyAKIyB0bXAuY250ID0gdGFibGUodG1wLmNvbXAkbWVtYmVyc2hpcCkKIyB0bXAuY250ID0gLXNvcnQoLXRtcC5jbnQpCgp0bXAuY250ID0gY250Cgpmb3IoayBpbiAxOmxlbmd0aCh0bXAuY250KSl7CiAgdG1wLmsgPSBhcy5udW1lcmljKG5hbWVzKHRtcC5jbnQpW2tdKQogIHRtcC5uYW1lcyA9IG5hbWVzKHRtcC5jb21wJG1lbWJlcnNoaXApW3RtcC5jb21wJG1lbWJlcnNoaXAgPT0gdG1wLmtdCiAgYi5ncmFwaC5zdWIgPSBzdi5lZGdlc1soc3YuZWRnZXNbLDFdICVpbiUgdG1wLm5hbWVzKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgIChzdi5lZGdlc1ssMl0gJWluJSB0bXAubmFtZXMpLF0KICAKICAKICBnLnBhcnQuc3ViIDwtIG5ldHdvcmsoYi5ncmFwaC5zdWIsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQogIGIuZ3JhcGgubmFtZXMuc3ViID0gbmV0d29yay52ZXJ0ZXgubmFtZXMoZy5wYXJ0LnN1YikKICAKICAKICAgIAogIGIuZ3JhcGguc2l6ZS5zdWIgPC0gYXMubnVtZXJpYyhzdWIoIi4qXFx8IiwgIiIsIGIuZ3JhcGgubmFtZXMuc3ViKSkKICBuYW1lcyhiLmdyYXBoLnNpemUuc3ViKSA9IGIuZ3JhcGgubmFtZXMuc3ViCiAgIyBiLmdyYXBoLnNpemUuc3ViID0gY2VpbGluZyhsb2coYi5ncmFwaC5zaXplLnN1YiwgMTApKQogIAogIGlmKChsZW5ndGgodW5pcXVlKCBnLm5vZGVzLnByb3RbYi5ncmFwaC5uYW1lcy5zdWJdKSkgPT0gMSkpewogICAgc2V0LnNlZWQoMjApCiAgICBwIDwtIGdnbmV0MihnLnBhcnQuc3ViLCBsYWJlbCA9IGIuZ3JhcGguc2l6ZS5zdWJbYi5ncmFwaC5uYW1lcy5zdWJdLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICAgICBub2RlLnNpemUgPSAxNSwKICAgICAgICAgICAgICAgIGFycm93LmdhcCA9IDAuMDcsIGFycm93LnNpemUgPSAzLAogICAgICAgICAgICAgICAgY29sb3IgPSBnLmNvbHNbZy5ub2Rlcy5wcm90W2IuZ3JhcGgubmFtZXMuc3ViXVsxXV0sCiAgICAgICAgICAgICAgICApICsgZ3VpZGVzKHNpemUgPSBGKSArICBnZ3RpdGxlKHBhc3RlKCdDb21wb25lbnQgIycsIHRtcC5rKSkKICAgIHAKICB9IGVsc2UgewogICAgc2V0LnNlZWQoMjApCiAgICBwIDwtIGdnbmV0MihnLnBhcnQuc3ViLCBsYWJlbCA9IGIuZ3JhcGguc2l6ZS5zdWJbYi5ncmFwaC5uYW1lcy5zdWJdLCBlZGdlLmNvbG9yID0gImJsYWNrIiwgCiAgICAgICAgICAgICAgICBub2RlLnNpemUgPSAxNSwKICAgICAgICAgICAgICAgIGFycm93LmdhcCA9IDAuMDcsIGFycm93LnNpemUgPSAzLAogICAgICAgICAgICAgICAgY29sb3IgPSBnLm5vZGVzLnByb3RbYi5ncmFwaC5uYW1lcy5zdWJdLAogICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGcuY29scywKICAgICAgICAgICAgICAgICkgKyBndWlkZXMoc2l6ZSA9IEYpICsgIGdndGl0bGUocGFzdGUoJ0NvbXBvbmVudCAjJywgdG1wLmspKQogICAgcAogIH0KICAKIAogIAogIHBkZihwYXN0ZShwYXRoLmZpZ3VyZXMuZXhhbXBsZXMsICdncmFwaF9zdl9leGFtcGxlXycsaywnX2NvbXBfJyx0bXAuaywnLnBkZicsIHNlcCA9ICcnKSwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQogIHByaW50KHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKSAgICAgIyBQbG90IDEgLS0+IGluIHRoZSBmaXJzdCBwYWdlIG9mIFBERgogIGRldi5vZmYoKQogIAogICMgYW5ub3RhdGlvbgogIGFubm90LnRtcCA9IHN2LnByb3Rbc3YucHJvdCRuYW1lICVpbiUgYi5ncmFwaC5uYW1lcy5zdWIsXQogICMgYW5ub3QudG1wID0gYW5ub3QudG1wW2Fubm90LnRtcCR0cmFuc3BvcyA9PSAxLF0KICAKICB3cml0ZS50YWJsZShhbm5vdC50bXAsIHBhc3RlKHBhdGguZmlndXJlcy5leGFtcGxlcywgJ2dyYXBoX3N2X2V4YW1wbGVfJyxrLCdfcGJsYXN0LnR4dCcsIHNlcCA9ICcnKSwgCiAgICAgICAgICAgICAgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRiwgcXVvdGUgPSBGLCBzZXAgPSAnXHQnKQogIAogIAogICMgaWYgRURUQSBhbm5vdGF0aW9uIGV4aXN0cwogIHN2LnRtcCA9IHVuaXF1ZShjKGIuZ3JhcGguc3ViKSkKICBzdi50bXAuY3V0IDwtIGdzdWIoIlxcfC4qIiwgIiIsIHN2LnRtcCkKICBzdi5hbm5vdC50bXAgPSBzdi5hbm5vdFtzdi50bXAuY3V0LF0KICBuLmZpeCA9IDkKICBzdi5hbm5vdC50bXAgID0gc3YuYW5ub3QudG1wWyxjKDE6bi5maXgsbi5maXgrd2hpY2goY29sU3Vtcyhzdi5hbm5vdC50bXBbLChuLmZpeCsxKTpuY29sKHN2LmFubm90LnRtcCldKSAhPSAwKSldCiAgcm93bmFtZXMoc3YuYW5ub3QudG1wKSA9IHN2LnRtcAogICAgCiAgd3JpdGUudGFibGUoc3YuYW5ub3QudG1wLCBwYXN0ZShwYXRoLmZpZ3VyZXMuZXhhbXBsZXMsICdncmFwaF9zdl9leGFtcGxlXycsaywnX2VkdGEudHh0Jywgc2VwID0gJycpLCAKICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsIHF1b3RlID0gRiwgc2VwID0gJ1x0JykKICAKICAjIENvcHkwTnVtYmVyIHZhcmlhdGlvbgogIGNudi50bXAgPSBjbnZbc3YudG1wLF0KICAKICBoZWF0bWFwKGNudi50bXAsIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCAicmVkIikpKDIwKSkKICAKfQoKYGBgCiMgUGllLWNoYXJ0IG9mIHByb3RlaW5zCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgdHlwZSA9IGMoIm5vIHByb3RlaW5zIiwgIlRFLXJlbGF0ZWQiLCAi0JrQsNGC0LXQs9C+0YDQuNGPIDIiLCAi0JrQsNGC0LXQs9C+0YDQuNGPIDMiLCAi0JrQsNGC0LXQs9C+0YDQuNGPIDQiKSwKICB2YWx1ZSA9IGMoMTM1LCA2MywgODUsIDEzMykKKQoKcGllLmNoYXJ0IDwtIGdncGxvdChkYXRhLCBhZXMoeCA9ICIiLCB5ID0gdmFsdWUsIGZpbGwgPSB0eXBlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogIHRoZW1lX3ZvaWQoKQoKcGllLmNoYXJ0CgpgYGAKCiMjIEFkbWl4dHVyZSBncm91cHMKYGBge3J9Cmdyb3VwcyA8LSBjKAogICJnZXJtYW55IiwKICAic291dGhfc3dlZGVuIiwKICAibm9ydGhfc3dlZGVuIiwKICAic291dGhfc3dlZGVuIiwKICAibm9ydGhfc3dlZGVuIiwKICAiZ2VybWFueSIsCiAgIndlc3Rlcm5fZXVyb3BlIiwKICAiY2VudHJhbF9ldXJvcGUiLAogICJpdGFseV9iYWxrYW5fY2F1Y2FzdXMiLAogICJzcGFpbiIsCiAgInJlbGljdCIsCiAgImFzaWEiLAogICJjZW50cmFsX2V1cm9wZSIsCiAgImFkbWl4ZWQiLAogICJzcGFpbiIsCiAgInJlbGljdCIsCiAgIml0YWx5X2JhbGthbl9jYXVjYXN1cyIsCiAgIndlc3Rlcm5fZXVyb3BlIiwKICAiYXNpYSIsCiAgImFmcmljYSIsCiAgImNoaW5hIiwKICAiY2hpbmEiLAogICJhZnJpY2EiLAogICJhZnJpY2EiLAogICJtYWRlaXJhIiwKICAibWFkZWlyYSIsCiAgImFmcmljYSIKKQoKIyDQmNGB0L/QvtC70YzQt9GD0LXQvCDRhNGD0L3QutGG0LjRjiB0YWJsZSgpINC00LvRjyDQv9C+0LTRgdGH0LXRgtCwINC60L7Qu9C40YfQtdGB0YLQstCwINGN0LvQtdC80LXQvdGC0L7QsiDQsiDQutCw0LbQtNC+0Lkg0LPRgNGD0L/Qv9C1CmFzLm1hdHJpeCh0YWJsZShncm91cHMpKQpgYGAKCgoKIyBPTEQKYGBge3J9CnN1bnNldCA8LSBjb2xvdXIoInN1bnNldCIpCmRpc2NyZXRlX3JhaW5ib3cgPC0gY29sb3VyKCJkaXNjcmV0ZSByYWluYm93IikKCmZpbGUudGUgPSAnL1ZvbHVtZXMvU2Ftc3VuZ19UNS92aWVubi93b3JrL2JsYXN0X3Rlc19hbm4udHh0JwpzaW0uY3V0b2ZmID0gMC44NQpsZW4uY3V0b2ZmID0gMTAwCmBgYAoKCmBgYHtyfQoKYiA9IHJlYWQudGFibGUoZmlsZS50ZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmIgPSBiW2IkVjEgIT0gYiRWOCxdCmIkbGVuMSA9IGFzLm51bWVyaWMoc2FwcGx5KGIkVjEsIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzddKSkKYiRsZW4yID0gYXMubnVtZXJpYyhzYXBwbHkoYiRWOCwgZnVuY3Rpb24ocykgc3Ryc3BsaXQocywgJ1xcfCcpW1sxXV1bN10pKQpiID0gYltiJGxlbjEgPj0gbGVuLmN1dG9mZixdCmIgPSBiW2IkbGVuMiA+PSBsZW4uY3V0b2ZmLF0KYiRjb21iID0gcGFzdGUoYiRWMSwgYiRWOCwgc2VwID0gJ14nKQoKIyBPcmRlciBwb3NpdGlvbnMgaW4gYmFzZQppZHggPSBiJFY0ID4gYiRWNQp0bXAgPSBiW2lkeCwgJ1Y0J10KYltpZHgsICdWNCddID0gYltpZHgsICdWNSddCmJbaWR4LCAnVjUnXSA9IHRtcAoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEdldCBzZXBhcmF0ZWx5IHRob3NlLCB3aG8gaGFzIGEgdW5pcXVlIGNvdmVyYWdlCmNvbWIudGJsID0gdGFibGUoYiRjb21iKQppZHgudW5pID0gYiRjb21iICVpbiUgbmFtZXMoY29tYi50YmwpW2NvbWIudGJsID09IDFdCmIudW5pID0gYltpZHgudW5pLF0KYiA9IGJbIWlkeC51bmksXQoKIyBUaGlzIHZhcmlhYmxlIHdpbGwgYmUgdXNlZCBsYXRlcgpiLnVuaSRwMSA9IChiLnVuaSRWMyAtIGIudW5pJFYyICsgMSkgLyBiLnVuaSRsZW4xCmIudW5pJHAyID0gKGIudW5pJFY1IC0gYi51bmkkVjQgKyAxKSAvIGIudW5pJGxlbjIKYi51bmkgPSBiLnVuaVsoYi51bmkkcDEgPj0gc2ltLmN1dG9mZikgfCAoYi51bmkkcDIgPj0gc2ltLmN1dG9mZiksXQoKYi5yZWxhdGlvbnMgPSBkYXRhLmZyYW1lKHN1Yi50ZSA9IGIudW5pJFYxW2IudW5pJHAxID49IHNpbS5jdXRvZmZdLAogICAgICAgICAgICAgICAgICAgICAgICAgdGUgPSBiLnVuaSRWOFtiLnVuaSRwMSA+PSBzaW0uY3V0b2ZmXSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmIucmVsYXRpb25zID0gcmJpbmQoYi5yZWxhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShzdWIudGUgPSBiLnVuaSRWOFtiLnVuaSRwMiA+PSBzaW0uY3V0b2ZmXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi51bmkkVjFbYi51bmkkcDIgPj0gc2ltLmN1dG9mZl0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSkKYi5yZWxhdGlvbnMgPSB1bmlxdWUoYi5yZWxhdGlvbnMpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgTWluLW1heCBvZiB0aGUgY292ZXJhZ2UgdG8gcmVtb3ZlIHRob3NlLCB3aG8gYXJlIE5PVCBpbiBlYWNoIG90aGVyIGNvbXBsZXRlbHkKYi5jb3YgPSB0YXBwbHkoYiRWMiwgYiRjb21iLCBtaW4pCmIuY292ID0gZGF0YS5mcmFtZShjb21iID0gbmFtZXMoYi5jb3YpLCBWMiA9IGIuY292KQpiLmNvdiRWMyA9IHRhcHBseShiJFYzLCBiJGNvbWIsIG1heCkKYi5jb3YkVjQgPSB0YXBwbHkoYiRWNCwgYiRjb21iLCBtaW4pCmIuY292JFY1ID0gdGFwcGx5KGIkVjUsIGIkY29tYiwgbWF4KQpiLmNvdiRsZW4xID0gdGFwcGx5KGIkbGVuMSwgYiRjb21iLCB1bmlxdWUpCmIuY292JGxlbjIgPSB0YXBwbHkoYiRsZW4yLCBiJGNvbWIsIHVuaXF1ZSkKYi5jb3YkcDEgPSAoYi5jb3YkVjMgLSBiLmNvdiRWMiArIDEpIC8gYi5jb3YkbGVuMQpiLmNvdiRwMiA9IChiLmNvdiRWNSAtIGIuY292JFY0ICsgMSkgLyBiLmNvdiRsZW4yCgpjb21iLnVuY292ID0gYi5jb3YkY29tYlsoYi5jb3YkcDEgPCBzaW0uY3V0b2ZmKSAmIChiLmNvdiRwMiA8IHNpbS5jdXRvZmYpXQoKYiA9IGJbIShiJGNvbWIgJWluJSBjb21iLnVuY292KSxdCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQ2FsY3VsYXRlIHRoZSBjb3ZlcmFnZSBkaXJlY3RseSBmb3IgdGhlIGZpcnN0CmIgPSBiW29yZGVyKGIkVjMpLF0KYiA9IGJbb3JkZXIoYiRWMiksXQpiID0gYltvcmRlcihiJGNvbWIpLF0KCiMgUmVtb3ZlIG5lc3RlZAppZHggPSB3aGljaCgoYiRWM1stbnJvdyhiKV0gPiBiJFYzWy0xXSkgJiAoYiRjb21iWy1ucm93KGIpXSA9PSBiJGNvbWJbLTFdKSkgKyAxCmIxID0gYlstaWR4LF0KCiMgQ29tcHV0ZSBnYXBzCmIxJGdhcCA9IGMoYjEkVjJbLTFdIC0gYjEkVjNbLW5yb3coYjEpXSAtIDEsIDApCmIxJGdhcFtiMSRnYXAgPCAwXSA9IDAKaWR4LmRpZmYuY29tYiA9IHdoaWNoKGIxJGNvbWJbLTFdICE9IGIxJGNvbWJbLW5yb3coYjEpXSkKYjEkZ2FwW2lkeC5kaWZmLmNvbWJdID0gMAoKYi5jb3YgPSB0YXBwbHkoYjEkVjIsIGIxJGNvbWIsIG1pbikKYi5jb3YgPSBkYXRhLmZyYW1lKGNvbWIgPSBuYW1lcyhiLmNvdiksIFYyID0gYi5jb3YpCmIuY292JFYzID0gdGFwcGx5KGIxJFYzLCBiMSRjb21iLCBtYXgpCmIuY292JGxlbjEgPSB0YXBwbHkoYjEkbGVuMSwgYjEkY29tYiwgdW5pcXVlKQpiLmNvdiRnYXAgPSB0YXBwbHkoYjEkZ2FwLCBiMSRjb21iLCBzdW0pCmIuY292JGxlbjEgPSBiLmNvdiRsZW4xIApiLmNvdiRwMSA9IChiLmNvdiRWMyAtIGIuY292JFYyICsgMSAtIGIuY292JGdhcCkgLyBiLmNvdiRsZW4xCmIuY292JFYxID0gdGFwcGx5KGIxJFYxLCBiMSRjb21iLCB1bmlxdWUpCmIuY292JFY4ID0gdGFwcGx5KGIxJFY4LCBiMSRjb21iLCB1bmlxdWUpCgpiLmNvdiA9IGIuY292W2IuY292JHAxID49IHNpbS5jdXRvZmYsXQoKCmIucmVsYXRpb25zID0gcmJpbmQoYi5yZWxhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShzdWIudGUgPSBiLmNvdiRWMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlID0gYi5jb3YkVjgsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSkKCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQ2FsY3VsYXRlIHRoZSBjb3ZlcmFnZSBkaXJlY3RseSBmb3IgdGhlIHNlY29uZApiID0gYltvcmRlcihiJFY1KSxdCmIgPSBiW29yZGVyKGIkVjQpLF0KYiA9IGJbb3JkZXIoYiRjb21iKSxdCgojIFJlbW92ZSBuZXN0ZWQKaWR4ID0gd2hpY2goKGIkVjVbLW5yb3coYildID4gYiRWNVstMV0pICYgKGIkY29tYlstbnJvdyhiKV0gPT0gYiRjb21iWy0xXSkpICsgMQpiMSA9IGJbLWlkeCxdCgojIENvbXB1dGUgZ2FwcwpiMSRnYXAgPSBjKGIxJFY0Wy0xXSAtIGIxJFY1Wy1ucm93KGIxKV0gLSAxLCAwKQpiMSRnYXBbYjEkZ2FwIDwgMF0gPSAwCmlkeC5kaWZmLmNvbWIgPSB3aGljaChiMSRjb21iWy0xXSAhPSBiMSRjb21iWy1ucm93KGIxKV0pCmIxJGdhcFtpZHguZGlmZi5jb21iXSA9IDAKCmIuY292ID0gdGFwcGx5KGIxJFY0LCBiMSRjb21iLCBtaW4pCmIuY292ID0gZGF0YS5mcmFtZShjb21iID0gbmFtZXMoYi5jb3YpLCBWNCA9IGIuY292KQpiLmNvdiRWNSA9IHRhcHBseShiMSRWNSwgYjEkY29tYiwgbWF4KQpiLmNvdiRsZW4yID0gdGFwcGx5KGIxJGxlbjIsIGIxJGNvbWIsIHVuaXF1ZSkKYi5jb3YkZ2FwID0gdGFwcGx5KGIxJGdhcCwgYjEkY29tYiwgc3VtKQpiLmNvdiRsZW4yID0gYi5jb3YkbGVuMiAKYi5jb3YkcDEgPSAoYi5jb3YkVjUgLSBiLmNvdiRWNCArIDEgLSBiLmNvdiRnYXApIC8gYi5jb3YkbGVuMgpiLmNvdiRWMSA9IHRhcHBseShiMSRWMSwgYjEkY29tYiwgdW5pcXVlKQpiLmNvdiRWOCA9IHRhcHBseShiMSRWOCwgYjEkY29tYiwgdW5pcXVlKQoKYi5jb3YgPSBiLmNvdltiLmNvdiRwMSA+PSBzaW0uY3V0b2ZmLF0KCgpiLnJlbGF0aW9ucyA9IHJiaW5kKGIucmVsYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoc3ViLnRlID0gYi5jb3YkVjgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZSA9IGIuY292JFYxLCBzdHJpbmdzQXNGYWN0b3JzID0gRikpCgogIApiLnJlbGF0aW9ucyA9IHVuaXF1ZShiLnJlbGF0aW9ucykKCgpiLnJlbGF0aW9ucwoKYGBgCgoKIyBEZWZpbmUgY2x1c3RlcnMKYGBge3J9CmIubm9kZXMgPSByYmluZChiLnJlbGF0aW9ucywKICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKHN1Yi50ZSA9IGIucmVsYXRpb25zJHRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGUgPSBiLnJlbGF0aW9ucyRzdWIudGUpKQoKYi5ub2RlcyRjb21iID0gcGFzdGUoYi5ub2RlcyRzdWIudGUsIGIubm9kZXMkdGUsIHNlcCA9ICdeJykKCmNvbWIudGJsID0gdGFibGUoYi5ub2RlcyRjb21iKQpjb21iLmJhY2suYW5kLmZvdGggPSBuYW1lcyhjb21iLnRibClbY29tYi50YmwgPj0gMl0KYi5ub2RlcyA9IGIubm9kZXNbYi5ub2RlcyRjb21iICVpbiUgY29tYi5iYWNrLmFuZC5mb3RoLF0KYi5ub2RlcyA9IHVuaXF1ZShiLm5vZGVzWywgYygnc3ViLnRlJywgJ3RlJyldKQoKCnRlLm5vZGVzIDwtIGlncmFwaDo6bWFrZV9ncmFwaCh0KGIubm9kZXMpLCBkaXJlY3RlZCA9IFQpCnRlLm5vZGVzIDwtIGlncmFwaDo6c2ltcGxpZnkodGUubm9kZXMpCnRlLm5vZGVzLmNvbXAgPC0gaWdyYXBoOjpjb21wb25lbnRzKHRlLm5vZGVzKQoKbm9kZXMgPSBwYXN0ZSgnTicsIHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCwgc2VwID0gJycpCm5hbWVzKG5vZGVzKSA9IG5hbWVzKHRlLm5vZGVzLmNvbXAkbWVtYmVyc2hpcCkKYGBgCgojIyBJZGVudGlmeSBmYW1pbHkgZm9yIGVhY2ggbm9kZQpgYGB7cn0KCm5vZGVzLmZhbWlseSA9IHNhcHBseShuYW1lcyhub2RlcyksIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzZdKQoKbm9kZXMuZmFtaWx5Lm1heCA9IHRhcHBseShub2Rlcy5mYW1pbHksIG5vZGVzLCBmdW5jdGlvbihzKXsKICB0YmwgPSB0YWJsZShzKQogIGYgPSBuYW1lcyh0YmwpW3RibCA9PSBtYXgodGJsKV0KICBpZihsZW5ndGgoZikgPT0gMSl7CiAgICByZXR1cm4oZikKICB9IGVsc2UgewogICAgcmV0dXJuKCdNaXgnKQogIH0KfSkKCm5vZGVzLmZhbWlseS5tYXhbbm9kZXMuZmFtaWx5Lm1heCAlaW4lIGMoJ0ROQS9Qb2dvJywgJ0ROQS9UYzEnLCAnRE5BL0hhcmJpbmdlcicsICdETkEvRW4tU3BtJywKICAgICAgICAgICAgICAgICAgICAgJ0ROQS9IQVQnLCAnRE5BJywgJ0ROQS9NYXJpbmVyJyldID0gJ0ROQScKbm9kZXMuZmFtaWx5Lm1heFtub2Rlcy5mYW1pbHkubWF4ICVpbiUgYygnUmF0aEUxX2NvbnMnLCAnUmF0aEUyX2NvbnMnKV0gPSAnRE5BJwpub2Rlcy5mYW1pbHkubWF4W25vZGVzLmZhbWlseS5tYXggJWluJSBjKCdMSU5FL0wxJywgJ0xJTkU/JyldID0gJ0xJTkUnCm5vZGVzLmZhbWlseS5tYXhbbm9kZXMuZmFtaWx5Lm1heCAlaW4lIGMoJ1VuYXNzaWduZWQnKV0gPSAnTWl4Jwpub2Rlcy5mYW1pbHkudW5pcXVlID0gdW5pcXVlKG5vZGVzLmZhbWlseS5tYXgpCgoKCmBgYAoKCiMjIEdyYXBoIHdpdGhvdXQgc2luZ2xldG9ucwpgYGB7cn0KCmIuZ3JhcGguaW5pdCA9IGIucmVsYXRpb25zWyhiLnJlbGF0aW9ucyRzdWIudGUgJWluJSBuYW1lcyhub2RlcykpICYgKGIucmVsYXRpb25zJHRlICVpbiUgbmFtZXMobm9kZXMpKSxdCmIuZ3JhcGggPSBiLmdyYXBoLmluaXQKYi5ncmFwaCA9IGNiaW5kKG5vZGVzW2FzLmNoYXJhY3RlcihiLmdyYXBoJHN1Yi50ZSldLCBub2Rlc1thcy5jaGFyYWN0ZXIoYi5ncmFwaCR0ZSldKQpiLmdyYXBoID0gdW5pcXVlKGIuZ3JhcGgpCgoKYi5ncmFwaCA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gIT0gYi5ncmFwaFssMl0sXQoKIyByZWR1Y2UgaW5kaXJlY3QgYXJyb3dzCmlkeC5yZW1vdmUgPSBjKCkKZm9yKGkuZWRnZSBpbiAxOm5yb3coYi5ncmFwaCkpewogIGlmKGkuZWRnZSAlJSAxMDAwID09IDApIHByaW50KGkuZWRnZSkKICB0bXAudG8gPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbaS5lZGdlLDFdLDJdCiAgdG1wLmZyb20gPSBiLmdyYXBoW2IuZ3JhcGhbLDJdID09IGIuZ3JhcGhbaS5lZGdlLDJdLDFdCiAgaWYobGVuZ3RoKGludGVyc2VjdCh0bXAudG8sIHRtcC5mcm9tKSkgPiAwKSBpZHgucmVtb3ZlID0gYyhpZHgucmVtb3ZlLCBpLmVkZ2UpCn0KaWR4LnJlbW92ZSA9IHVuaXF1ZShpZHgucmVtb3ZlKQpiLmdyYXBoID0gYi5ncmFwaFstaWR4LnJlbW92ZSxdCgoKIyB0ZS5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChiLmdyYXBoKSwgZGlyZWN0ZWQgPSBUKQojIHRlLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodGUuZ3JhcGgpCiMgdGUuZ3JhcGguY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModGUuZ3JhcGgpCgoKbm9kZXMuZmFtaWx5Lm1heC5ncmFwaCA9IG5vZGVzLmZhbWlseS5tYXhbbmFtZXMobm9kZXMuZmFtaWx5Lm1heCkgJWluJSB1bmlxdWUoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pKV0KCmdyYXBoLmNvbHMgPSBzdW5zZXQobGVuZ3RoKHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4LmdyYXBoKSkpCgpncmFwaC5jb2xzID0gZGlzY3JldGVfcmFpbmJvdyhsZW5ndGgodW5pcXVlKG5vZGVzLmZhbWlseS5tYXguZ3JhcGgpKSkKbmFtZXMoZ3JhcGguY29scykgPSB1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5ncmFwaCkKZy5wYXJ0IDwtIG5ldHdvcmsoYi5ncmFwaCwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IFRSVUUpCnAgPC0gZ2duZXQyKGcucGFydCwgbGFiZWwgPSBGQUxTRSwgZWRnZS5jb2xvciA9ICJibGFjayIsIG5vZGUuc2l6ZSA9IDEsIAogICAgICAgICAgICBjb2xvciA9IG5vZGVzLmZhbWlseS5tYXguZ3JhcGgsIHBhbGV0dGUgPSBncmFwaC5jb2xzLAogICAgICAgICAgICBtb2RlID0gImthbWFkYWthd2FpIikjICsgZ3VpZGVzKHNpemUgPSBGQUxTRSkKcAoKYGBgCiMjIEdyYXBoIFdJVEggc2luZ2xldG9ucwpgYGB7cn0KCgpuYW1lcy5jb3JlID0gbmFtZXMobm9kZXMuZmFtaWx5Lm1heC5ncmFwaCkKCmIuZ3JhcGguaW5pdCA9IGIucmVsYXRpb25zCmZvcihpIGluIDE6Mil7CiAgYi5ncmFwaC5pbml0W2IuZ3JhcGguaW5pdFssaV0gJWluJSBuYW1lcyhub2RlcyksIGldID0gbm9kZXNbYi5ncmFwaC5pbml0W2IuZ3JhcGguaW5pdFssaV0gJWluJSBuYW1lcyhub2RlcyksIGldXQp9CgpiLmdyYXBoID0gdW5pcXVlKGIuZ3JhcGguaW5pdCkKYi5ncmFwaCA9IGIuZ3JhcGhbYi5ncmFwaFssMV0gIT0gYi5ncmFwaFssMl0sXQpiLmdyYXBoID0gdW5pcXVlKGIuZ3JhcGgpCiMgVmVydGVjZXMgZnJvbSB0aGUgcHJldmlvdXMgZ3JhcGgKYi5ncmFwaCA9IGIuZ3JhcGhbKGIuZ3JhcGhbLDFdICVpbiUgbmFtZXMuY29yZSkgfCAoYi5ncmFwaFssMl0gJWluJSBuYW1lcy5jb3JlKSxdCgoKIyByZWR1Y2UgaW5kaXJlY3QgYXJyb3dzCmlkeC5yZW1vdmUgPSBjKCkKZm9yKGkuZWRnZSBpbiAxOm5yb3coYi5ncmFwaCkpewogIGlmKGkuZWRnZSAlJSAxMDAwID09IDApIHByaW50KGkuZWRnZSkKICB0bXAudG8gPSBiLmdyYXBoW2IuZ3JhcGhbLDFdID09IGIuZ3JhcGhbaS5lZGdlLDFdLDJdCiAgdG1wLmZyb20gPSBiLmdyYXBoW2IuZ3JhcGhbLDJdID09IGIuZ3JhcGhbaS5lZGdlLDJdLDFdCiAgaWYobGVuZ3RoKGludGVyc2VjdCh0bXAudG8sIHRtcC5mcm9tKSkgPiAwKSBpZHgucmVtb3ZlID0gYyhpZHgucmVtb3ZlLCBpLmVkZ2UpCn0KaWR4LnJlbW92ZSA9IHVuaXF1ZShpZHgucmVtb3ZlKQpiLmdyYXBoID0gYi5ncmFwaFstaWR4LnJlbW92ZSxdCgp0ZS5ncmFwaCA8LSBpZ3JhcGg6Om1ha2VfZ3JhcGgodChiLmdyYXBoKSwgZGlyZWN0ZWQgPSBUKQpkIDwtIGlncmFwaDo6ZGlzdGFuY2VzKHRlLmdyYXBoKQojIHRlLmdyYXBoIDwtIGlncmFwaDo6c2ltcGxpZnkodGUuZ3JhcGgpCiMgdGUuZ3JhcGguY29tcCA8LSBpZ3JhcGg6OmNvbXBvbmVudHModGUuZ3JhcGgpCgpuYW1lcy5uZXcgPSB1bmlxdWUoc2V0ZGlmZihjKGIuZ3JhcGhbLDFdLCBiLmdyYXBoWywyXSksIG5hbWVzKG5vZGVzLmZhbWlseS5tYXgpKSkKIyBuYW1lcy5uZXcudmFsID0gcGFzdGUoJ0cnLDE6bGVuZ3RoKG5hbWVzLm5ldyksIHNlcCA9ICcnKQojIG5hbWVzKG5hbWVzLm5ldy52YWwpID0gbmFtZXMubmV3CiMgbmFtZXMubmV3LnZhbCA9IAoKbmFtZXMubmV3LmZhbWlseSA9IHNhcHBseShuYW1lcy5uZXcsIGZ1bmN0aW9uKHMpIHN0cnNwbGl0KHMsICdcXHwnKVtbMV1dWzZdKQpuYW1lcy5uZXcuZmFtaWx5W25hbWVzLm5ldy5mYW1pbHkgJWluJSBjKCdETkEvUG9nbycsICdETkEvVGMxJywgJ0ROQS9IYXJiaW5nZXInLCAnRE5BL0VuLVNwbScsCiAgICAgICAgICAgICAgICAgICAgICdETkEvSEFUJywgJ0ROQScsICdETkEvTWFyaW5lcicpXSA9ICdETkEnCm5hbWVzLm5ldy5mYW1pbHlbbmFtZXMubmV3LmZhbWlseSAlaW4lIGMoJ1JhdGhFMV9jb25zJywgJ1JhdGhFMl9jb25zJyldID0gJ0ROQScKbmFtZXMubmV3LmZhbWlseVtuYW1lcy5uZXcuZmFtaWx5ICVpbiUgYygnTElORS9MMScsICdMSU5FPycpXSA9ICdMSU5FJwpuYW1lcy5uZXcuZmFtaWx5W25hbWVzLm5ldy5mYW1pbHkgJWluJSBjKCdVbmFzc2lnbmVkJyldID0gJ01peCcKCgpub2Rlcy5mYW1pbHkubWF4LmFkZCA9IGMobm9kZXMuZmFtaWx5Lm1heCwgbmFtZXMubmV3LmZhbWlseSkKbm9kZXMuZmFtaWx5Lm1heC5hZGQgPSBub2Rlcy5mYW1pbHkubWF4LmFkZFt1bmlxdWUoYyhiLmdyYXBoWywxXSwgYi5ncmFwaFssMl0pKV0KCmdyYXBoLmNvbHMgPSBkaXNjcmV0ZV9yYWluYm93KGxlbmd0aCh1bmlxdWUobm9kZXMuZmFtaWx5Lm1heC5hZGQpKSkKZ3JhcGguY29scyA9IHNhbXBsZShncmFwaC5jb2xzKQpuYW1lcyhncmFwaC5jb2xzKSA9IHVuaXF1ZShub2Rlcy5mYW1pbHkubWF4LmFkZCkKCmcucGFydCA8LSBuZXR3b3JrKGIuZ3JhcGgsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBUUlVFKQpwIDwtIGdnbmV0MihnLnBhcnQsIGxhYmVsID0gRkFMU0UsIGVkZ2UuY29sb3IgPSAiYmxhY2siLCBub2RlLnNpemUgPSAwLjUsIAogICAgICAgICAgICBjb2xvciA9IG5vZGVzLmZhbWlseS5tYXguYWRkLAogICAgICAgICAgICBwYWxldHRlID0gZ3JhcGguY29scywgbW9kZSA9ICJrYW1hZGFrYXdhaSIpCnAKYGBgCgojIFRTTkUKYGBge3J9CgoKbGlicmFyeShSdHNuZSkKCgoKCmQgPC0gaWdyYXBoOjpkaXN0YW5jZXModGUuZ3JhcGgpCmQubWF4ID0gbWF4KGRbIWlzLmluZmluaXRlKGQpXSkKCmRbaXMuaW5maW5pdGUoZCldID0gZC5tYXggKiAxLjMKCnRTTkUgPC0gUnRzbmUoZCwgaXNfZGlzdGFuY2UgPSBUUlVFLCBkaW1zID0gMikKCnBsb3QodFNORSRZWywxXSwgdFNORSRZWywyXSkKCmBgYAoKCgo=